UpdateGlue.cxx 3.91 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 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"
Max Kellermann's avatar
Max Kellermann committed
32
#include "Main.hxx"
33
#include "Instance.hxx"
34
#include "system/FatalError.hxx"
35
#include "thread/Id.hxx"
36
#include "thread/Thread.hxx"
37
#include "thread/Util.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 73
	SetThreadIdlePriority();

74
	modified = update_walk(next.path_utf8.c_str(), next.discard);
75

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

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

88
	progress = UPDATE_PROGRESS_DONE;
89
	GlobalEvents::Emit(GlobalEvents::UPDATE);
90 91
}

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

	progress = UPDATE_PROGRESS_RUNNING;
98
	modified = false;
99

100 101
	next = std::move(i);

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

106
	FormatDebug(update_domain,
107 108 109 110 111 112 113 114 115 116
		    "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;
117 118
}

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

124
	if (!db_is_simple() || !mapper_has_music_directory())
125 126
		return 0;

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

132 133
		update_task_id = id;
		return id;
134
	}
135

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

	idle_add(IDLE_UPDATE);

141
	return id;
142 143
}

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

152
	update_thread.Join();
153
	next = UpdateQueueItem();
154

155 156
	idle_add(IDLE_UPDATE);

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

161 162
	auto i = update_queue_shift();
	if (i.IsDefined()) {
163
		/* schedule the next path */
164
		spawn_update_task(std::move(i));
165 166 167 168
	} else {
		progress = UPDATE_PROGRESS_IDLE;
	}
}
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
}