MultiSocketMonitor.hxx 5.92 KB
Newer Older
1
/*
2
 * Copyright 2003-2018 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * http://www.musicpd.org
 *
 * 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.
 *
 * 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.
 */

#ifndef MPD_MULTI_SOCKET_MONITOR_HXX
#define MPD_MULTI_SOCKET_MONITOR_HXX

23
#include "IdleMonitor.hxx"
24
#include "TimerEvent.hxx"
25
#include "SocketMonitor.hxx"
26
#include "util/Compiler.h"
27

28
#include <forward_list>
29
#include <iterator>
30 31 32

#include <assert.h>

33
#ifndef _WIN32
34 35 36
struct pollfd;
#endif

37 38 39
class EventLoop;

/**
40 41 42 43
 * Similar to #SocketMonitor, but monitors multiple sockets.  To use
 * it, implement the methods PrepareSockets() and DispatchSockets().
 * In PrepareSockets(), use UpdateSocketList() and AddSocket().
 * DispatchSockets() will be called if at least one socket is ready.
44
 */
45
class MultiSocketMonitor : IdleMonitor
46 47 48 49 50 51 52
{
	class SingleFD final : public SocketMonitor {
		MultiSocketMonitor &multi;

		unsigned revents;

	public:
53
		SingleFD(MultiSocketMonitor &_multi, SocketDescriptor _fd,
54
			 unsigned events) noexcept
55 56 57 58 59
			:SocketMonitor(_fd, _multi.GetEventLoop()),
			multi(_multi), revents(0) {
			Schedule(events);
		}

60
		SocketDescriptor GetSocket() const noexcept {
61
			return SocketMonitor::GetSocket();
62 63
		}

64
		unsigned GetEvents() const noexcept {
65 66 67
			return SocketMonitor::GetScheduledFlags();
		}

68
		void SetEvents(unsigned _events) noexcept {
69 70 71 72
			revents &= _events;
			SocketMonitor::Schedule(_events);
		}

73
		unsigned GetReturnedEvents() const noexcept {
74 75 76
			return revents;
		}

77
		void ClearReturnedEvents() noexcept {
78 79 80 81
			revents = 0;
		}

	protected:
82
		bool OnSocketReady(unsigned flags) noexcept override {
83 84 85 86 87 88 89 90
			revents = flags;
			multi.SetReady();
			return true;
		}
	};

	friend class SingleFD;

91 92
	TimerEvent timeout_event;

93 94 95
	/**
	 * DispatchSockets() should be called.
	 */
96
	bool ready = false;
97 98 99 100 101 102 103 104 105

	/**
	 * PrepareSockets() should be called.
	 *
	 * Note that this doesn't need to be initialized by the
	 * constructor; this class is activated with the first
	 * InvalidateSockets() call, which initializes this flag.
	 */
	bool refresh;
106

107
	std::forward_list<SingleFD> fds;
108 109

public:
110 111 112 113
	static constexpr unsigned READ = SocketMonitor::READ;
	static constexpr unsigned WRITE = SocketMonitor::WRITE;
	static constexpr unsigned ERROR = SocketMonitor::ERROR;
	static constexpr unsigned HANGUP = SocketMonitor::HANGUP;
114

115
	MultiSocketMonitor(EventLoop &_loop) noexcept;
116

117
	using IdleMonitor::GetEventLoop;
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	/**
	 * Clear the socket list and disable all #EventLoop
	 * registrations.  Run this in the #EventLoop thread before
	 * destroying this object.
	 *
	 * Later, this object can be reused and reactivated by calling
	 * InvalidateSockets().
	 *
	 * Note that this class doesn't have a destructor which calls
	 * this method, because this would be racy and thus pointless:
	 * at the time ~MultiSocketMonitor() is called, our virtual
	 * methods have been morphed to be pure again, and in the
	 * meantime the #EventLoop thread could invoke those pure
	 * methods.
	 */
134
	void Reset() noexcept;
135

136 137 138 139
	/**
	 * Invalidate the socket list.  A call to PrepareSockets() is
	 * scheduled which will then update the list.
	 */
140
	void InvalidateSockets() noexcept {
141 142
		refresh = true;
		IdleMonitor::Schedule();
143 144
	}

145 146 147 148 149
	/**
	 * Add one socket to the list of monitored sockets.
	 *
	 * May only be called from PrepareSockets().
	 */
150
	void AddSocket(SocketDescriptor fd, unsigned events) noexcept {
151
		fds.emplace_front(*this, fd, events);
152 153
	}

154 155
	/**
	 * Remove all sockets.
156 157
	 *
	 * May only be called from PrepareSockets().
158
	 */
159
	void ClearSocketList() noexcept;
160

161 162 163 164 165
	/**
	 * Update the known sockets by invoking the given function for
	 * each one; its return value is the events bit mask.  A
	 * return value of 0 means the socket will be removed from the
	 * list.
166 167
	 *
	 * May only be called from PrepareSockets().
168
	 */
169
	template<typename E>
170
	void UpdateSocketList(E &&e) noexcept {
171 172 173
		for (auto prev = fds.before_begin(), end = fds.end(),
			     i = std::next(prev);
		     i != end; i = std::next(prev)) {
174
			assert(i->GetEvents() != 0);
175

176
			unsigned events = e(i->GetSocket());
177
			if (events != 0) {
178
				i->SetEvents(events);
179 180 181 182 183 184 185
				prev = i;
			} else {
				fds.erase_after(prev);
			}
		}
	}

186
#ifndef _WIN32
187 188 189
	/**
	 * Replace the socket list with the given file descriptors.
	 * The given pollfd array will be modified by this method.
190 191
	 *
	 * May only be called from PrepareSockets().
192
	 */
193
	void ReplaceSocketList(pollfd *pfds, unsigned n) noexcept;
194 195
#endif

196 197 198 199 200 201 202 203 204 205 206 207 208
	/**
	 * Invoke a function for each socket which has become ready.
	 */
	template<typename F>
	void ForEachReturnedEvent(F &&f) noexcept {
		for (auto &i : fds) {
			if (i.GetReturnedEvents() != 0) {
				f(i.GetSocket(), i.GetReturnedEvents());
				i.ClearReturnedEvents();
			}
		}
	}

209
protected:
210
	/**
211 212 213 214
	 * Override this method and update the socket registrations.
	 * To do that, call AddSocket(), ClearSocketList(),
	 * UpdateSocketList() and ReplaceSocketList().
	 *
215
	 * @return timeout or a negative value for no timeout
216
	 */
217
	virtual std::chrono::steady_clock::duration PrepareSockets() noexcept = 0;
218 219 220 221 222

	/**
	 * At least one socket is ready or the timeout has expired.
	 * This method should be used to perform I/O.
	 */
223
	virtual void DispatchSockets() noexcept = 0;
224

225
private:
226
	void SetReady() noexcept {
227 228 229 230
		ready = true;
		IdleMonitor::Schedule();
	}

231
	void Prepare() noexcept;
232

233
	void OnTimeout() noexcept {
234 235 236 237
		SetReady();
		IdleMonitor::Schedule();
	}

238
	virtual void OnIdle() noexcept final;
239 240 241
};

#endif