event_pipe.c 3.95 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2011 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 "event_pipe.h"
22
#include "fd_util.h"
23
#include "mpd_error.h"
24

25
#include <stdbool.h>
26
#include <assert.h>
27
#include <glib.h>
28
#include <string.h>
29 30 31
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
32

33 34 35 36 37
#ifdef WIN32
/* for _O_BINARY */
#include <fcntl.h>
#endif

38 39 40
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "event_pipe"

41
static int event_pipe[2];
42
static GIOChannel *event_channel;
43
static guint event_pipe_source_id;
44 45 46
static GMutex *event_pipe_mutex;
static bool pipe_events[PIPE_EVENT_MAX];
static event_pipe_callback_t event_pipe_callbacks[PIPE_EVENT_MAX];
47

48 49 50 51 52 53 54 55 56 57 58 59
/**
 * Invoke the callback for a certain event.
 */
static void
event_pipe_invoke(enum pipe_event event)
{
	assert((unsigned)event < PIPE_EVENT_MAX);
	assert(event_pipe_callbacks[event] != NULL);

	event_pipe_callbacks[event]();
}

60 61 62 63
static gboolean
main_notify_event(G_GNUC_UNUSED GIOChannel *source,
		  G_GNUC_UNUSED GIOCondition condition,
		  G_GNUC_UNUSED gpointer data)
64
{
65
	char buffer[256];
66 67 68 69 70 71
	gsize bytes_read;
	GError *error = NULL;
	GIOStatus status = g_io_channel_read_chars(event_channel,
						   buffer, sizeof(buffer),
						   &bytes_read, &error);
	if (status == G_IO_STATUS_ERROR)
72
		MPD_ERROR("error reading from pipe: %s", error->message);
73

74
	bool events[PIPE_EVENT_MAX];
75 76 77 78 79 80
	g_mutex_lock(event_pipe_mutex);
	memcpy(events, pipe_events, sizeof(events));
	memset(pipe_events, 0, sizeof(pipe_events));
	g_mutex_unlock(event_pipe_mutex);

	for (unsigned i = 0; i < PIPE_EVENT_MAX; ++i)
81 82
		if (events[i])
			/* invoke the event handler */
83
			event_pipe_invoke(i);
84

85
	return true;
86 87
}

88
void event_pipe_init(void)
89
{
90
	GIOChannel *channel;
91
	int ret;
92

93
	ret = pipe_cloexec_nonblock(event_pipe);
94
	if (ret < 0)
95
		MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
96

97 98 99 100 101
#ifndef G_OS_WIN32
	channel = g_io_channel_unix_new(event_pipe[0]);
#else
	channel = g_io_channel_win32_new_fd(event_pipe[0]);
#endif
102 103
	g_io_channel_set_encoding(channel, NULL, NULL);
	g_io_channel_set_buffered(channel, false);
104

105 106
	event_pipe_source_id = g_io_add_watch(channel, G_IO_IN,
					      main_notify_event, NULL);
107 108

	event_channel = channel;
109

110
	event_pipe_mutex = g_mutex_new();
111 112
}

113
void event_pipe_deinit(void)
114
{
115 116
	g_mutex_free(event_pipe_mutex);

117
	g_source_remove(event_pipe_source_id);
118
	g_io_channel_unref(event_channel);
119

120 121
#ifndef WIN32
	/* By some strange reason this call hangs on Win32 */
122
	close(event_pipe[0]);
123
#endif
124
	close(event_pipe[1]);
125 126
}

127 128
void
event_pipe_register(enum pipe_event event, event_pipe_callback_t callback)
129
{
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	assert((unsigned)event < PIPE_EVENT_MAX);
	assert(event_pipe_callbacks[event] == NULL);

	event_pipe_callbacks[event] = callback;
}

void event_pipe_emit(enum pipe_event event)
{
	ssize_t w;

	assert((unsigned)event < PIPE_EVENT_MAX);

	g_mutex_lock(event_pipe_mutex);
	if (pipe_events[event]) {
		/* already set: don't write */
		g_mutex_unlock(event_pipe_mutex);
		return;
	}

	pipe_events[event] = true;
	g_mutex_unlock(event_pipe_mutex);

	w = write(event_pipe[1], "", 1);
153
	if (w < 0 && errno != EAGAIN && errno != EINTR)
154
		MPD_ERROR("error writing to pipe: %s", strerror(errno));
155 156
}

157 158 159 160 161
void event_pipe_emit_fast(enum pipe_event event)
{
	assert((unsigned)event < PIPE_EVENT_MAX);

	pipe_events[event] = true;
162
	(void)write(event_pipe[1], "", 1);
163
}