UpdateGlue.cxx 3.58 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"
21
#include "Service.hxx"
22
#include "UpdateDomain.hxx"
23
#include "db/DatabaseListener.hxx"
24
#include "db/plugins/SimpleDatabasePlugin.hxx"
Max Kellermann's avatar
Max Kellermann committed
25
#include "Idle.hxx"
26
#include "util/Error.hxx"
27
#include "Log.hxx"
28
#include "Instance.hxx"
29
#include "system/FatalError.hxx"
30
#include "thread/Id.hxx"
31
#include "thread/Thread.hxx"
32
#include "thread/Util.hxx"
33

34 35 36 37
#ifndef NDEBUG
#include "event/Loop.hxx"
#endif

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

40 41
inline void
UpdateService::Task()
42
{
43 44 45
	if (!next.path_utf8.empty())
		FormatDebug(update_domain, "starting: %s",
			    next.path_utf8.c_str());
46
	else
47
		LogDebug(update_domain, "starting");
48

49 50
	SetThreadIdlePriority();

51
	modified = walk.Walk(*db.GetRoot(), next.path_utf8.c_str(),
52
			     next.discard);
53

54
	if (modified || !db.FileExists()) {
55
		Error error;
56
		if (!db.Save(error))
57
			LogError(error, "Failed to save database");
58
	}
59

60 61 62
	if (!next.path_utf8.empty())
		FormatDebug(update_domain, "finished: %s",
			    next.path_utf8.c_str());
63
	else
64
		LogDebug(update_domain, "finished");
65

66
	progress = UPDATE_PROGRESS_DONE;
67
	DeferredMonitor::Schedule();
68 69
}

70 71 72 73 74 75 76 77 78
void
UpdateService::Task(void *ctx)
{
	UpdateService &service = *(UpdateService *)ctx;
	return service.Task();
}

void
UpdateService::StartThread(UpdateQueueItem &&i)
79
{
80
	assert(GetEventLoop().IsInsideOrNull());
81 82

	progress = UPDATE_PROGRESS_RUNNING;
83
	modified = false;
84

85 86
	next = std::move(i);

87
	Error error;
88
	if (!update_thread.Start(Task, this, error))
89
		FatalError(error);
90

91
	FormatDebug(update_domain,
92 93 94
		    "spawned thread for update job id %i", next.id);
}

95 96
unsigned
UpdateService::GenerateId()
97 98 99 100 101
{
	unsigned id = update_task_id + 1;
	if (id > update_task_id_max)
		id = 1;
	return id;
102 103
}

104
unsigned
105
UpdateService::Enqueue(const char *path, bool discard)
106
{
107
	assert(GetEventLoop().IsInsideOrNull());
108 109

	if (progress != UPDATE_PROGRESS_IDLE) {
110 111
		const unsigned id = GenerateId();
		if (!queue.Push(path, discard, id))
112
			return 0;
113

114 115
		update_task_id = id;
		return id;
116
	}
117

118 119
	const unsigned id = update_task_id = GenerateId();
	StartThread(UpdateQueueItem(path, discard, id));
120 121 122

	idle_add(IDLE_UPDATE);

123
	return id;
124 125
}

126 127 128
/**
 * Called in the main thread after the database update is finished.
 */
129 130
void
UpdateService::RunDeferred()
131 132
{
	assert(progress == UPDATE_PROGRESS_DONE);
133
	assert(next.IsDefined());
134

135
	update_thread.Join();
136
	next = UpdateQueueItem();
137

138 139
	idle_add(IDLE_UPDATE);

140
	if (modified)
141
		/* send "idle" events */
142
		listener.OnDatabaseModified();
143

144
	auto i = queue.Pop();
145
	if (i.IsDefined()) {
146
		/* schedule the next path */
147
		StartThread(std::move(i));
148 149 150 151
	} else {
		progress = UPDATE_PROGRESS_IDLE;
	}
}
152

153
UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db,
154
			     Storage &_storage,
155 156 157
			     DatabaseListener &_listener)
	:DeferredMonitor(_loop), db(_db), listener(_listener),
	 progress(UPDATE_PROGRESS_IDLE),
158
	 update_task_id(0),
159
	 walk(_loop, _listener, _storage)
160 161
{
}