UpdateGlue.cxx 3.89 KB
Newer Older
1
/*
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
3
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19
 */

20
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
21 22 23 24
#include "UpdateGlue.hxx"
#include "UpdateQueue.hxx"
#include "UpdateWalk.hxx"
#include "UpdateRemove.hxx"
25
#include "UpdateDomain.hxx"
Max Kellermann's avatar
Max Kellermann committed
26
#include "Mapper.hxx"
27
#include "DatabaseSimple.hxx"
Max Kellermann's avatar
Max Kellermann committed
28
#include "Idle.hxx"
29
#include "GlobalEvents.hxx"
30
#include "util/Error.hxx"
31
#include "Log.hxx"
32
#include "Stats.hxx"
Max Kellermann's avatar
Max Kellermann committed
33
#include "Main.hxx"
34
#include "Instance.hxx"
35
#include "system/FatalError.hxx"
36
#include "thread/Id.hxx"
37
#include "thread/Thread.hxx"
38

Max Kellermann's avatar
Max Kellermann committed
39
#include <assert.h>
40

41
static enum update_progress {
42 43 44 45 46
	UPDATE_PROGRESS_IDLE = 0,
	UPDATE_PROGRESS_RUNNING = 1,
	UPDATE_PROGRESS_DONE = 2
} progress;

47 48
static bool modified;

49
static Thread update_thread;
50

51
static const unsigned update_task_id_max = 1 << 15;
52

53
static unsigned update_task_id;
54

55
static UpdateQueueItem next;
56

57 58
unsigned
isUpdatingDB(void)
59
{
60
	return next.id;
61 62
}

63
static void
64
update_task(gcc_unused void *ctx)
65
{
66 67 68
	if (!next.path_utf8.empty())
		FormatDebug(update_domain, "starting: %s",
			    next.path_utf8.c_str());
69
	else
70
		LogDebug(update_domain, "starting");
71

72
	modified = update_walk(next.path_utf8.c_str(), next.discard);
73

74
	if (modified || !db_exists()) {
75 76
		Error error;
		if (!db_save(error))
77
			LogError(error, "Failed to save database");
78
	}
79

80 81 82
	if (!next.path_utf8.empty())
		FormatDebug(update_domain, "finished: %s",
			    next.path_utf8.c_str());
83
	else
84
		LogDebug(update_domain, "finished");
85

86
	progress = UPDATE_PROGRESS_DONE;
87
	GlobalEvents::Emit(GlobalEvents::UPDATE);
88 89
}

90
static void
91
spawn_update_task(UpdateQueueItem &&i)
92
{
93
	assert(main_thread.IsInside());
94 95

	progress = UPDATE_PROGRESS_RUNNING;
96
	modified = false;
97

98 99
	next = std::move(i);

100
	Error error;
101
	if (!update_thread.Start(update_task, nullptr, error))
102
		FatalError(error);
103

104
	FormatDebug(update_domain,
105 106 107 108 109 110 111 112 113 114
		    "spawned thread for update job id %i", next.id);
}

static unsigned
generate_update_id()
{
	unsigned id = update_task_id + 1;
	if (id > update_task_id_max)
		id = 1;
	return id;
115 116
}

117
unsigned
118
update_enqueue(const char *path, bool discard)
119
{
120
	assert(main_thread.IsInside());
121

122
	if (!db_is_simple() || !mapper_has_music_directory())
123 124
		return 0;

125
	if (progress != UPDATE_PROGRESS_IDLE) {
126 127
		const unsigned id = generate_update_id();
		if (!update_queue_push(path, discard, id))
128
			return 0;
129

130 131
		update_task_id = id;
		return id;
132
	}
133

134 135
	const unsigned id = update_task_id = generate_update_id();
	spawn_update_task(UpdateQueueItem(path, discard, id));
136 137 138

	idle_add(IDLE_UPDATE);

139
	return id;
140 141
}

142 143 144 145 146 147
/**
 * Called in the main thread after the database update is finished.
 */
static void update_finished_event(void)
{
	assert(progress == UPDATE_PROGRESS_DONE);
148
	assert(next.IsDefined());
149

150
	update_thread.Join();
151
	next = UpdateQueueItem();
152

153 154
	idle_add(IDLE_UPDATE);

155
	if (modified)
156
		/* send "idle" events */
157
		instance->DatabaseModified();
158

159 160
	auto i = update_queue_shift();
	if (i.IsDefined()) {
161
		/* schedule the next path */
162
		spawn_update_task(std::move(i));
163 164
	} else {
		progress = UPDATE_PROGRESS_IDLE;
165

166
		stats_update();
167 168
	}
}
169 170 171

void update_global_init(void)
{
172
	GlobalEvents::Register(GlobalEvents::UPDATE, update_finished_event);
173 174 175

	update_remove_global_init();
	update_walk_global_init();
176 177 178 179
}

void update_global_finish(void)
{
180
	update_walk_global_finish();
181
}