SocketEvent.hxx 4.57 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * 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.
 */

20 21
#ifndef MPD_SOCKET_EVENT_HXX
#define MPD_SOCKET_EVENT_HXX
22

23
#include "BackendEvents.hxx"
24
#include "net/SocketDescriptor.hxx"
25
#include "util/BindMethod.hxx"
26
#include "util/IntrusiveList.hxx"
27

28
#include <cstddef>
29 30
#include <type_traits>

31 32
class EventLoop;

33 34 35
/**
 * Monitor events on a socket.  Call Schedule() to announce events
 * you're interested in, or Cancel() to cancel your subscription.  The
36 37
 * #EventLoop will invoke the callback as soon as any of the
 * subscribed events are ready.
38
 *
39 40 41
 * This class does not feel responsible for closing the socket.  Call
 * Close() to do it manually.
 *
42 43 44
 * This class is not thread-safe, all methods must be called from the
 * thread that runs the #EventLoop, except where explicitly documented
 * as thread-safe.
45
 */
46 47
class SocketEvent final : IntrusiveListHook, public EventPollBackendEvents
{
48
	friend class EventLoop;
49
	friend class IntrusiveList<SocketEvent>;
50

51 52 53 54 55
	EventLoop &loop;

	using Callback = BoundMethod<void(unsigned events) noexcept>;
	const Callback callback;

56
	SocketDescriptor fd;
57 58

	/**
59
	 * A bit mask of events that are currently registered in the
60
	 * #EventLoop.
61
	 */
62
	unsigned scheduled_flags = 0;
63

64 65 66 67 68 69 70
	/**
	 * A bit mask of events which have been reported as "ready" by
	 * epoll_wait().  If non-zero, then the #EventLoop will call
	 * Dispatch() soon.
	 */
	unsigned ready_flags = 0;

71
public:
72 73 74 75 76 77
	/**
	 * These flags are always reported by epoll_wait() and don't
	 * need to be registered with epoll_ctl().
	 */
	static constexpr unsigned IMPLICIT_FLAGS = ERROR|HANGUP;

78
	using ssize_t = std::make_signed<size_t>::type;
79

80 81 82 83 84
	SocketEvent(EventLoop &_loop, Callback _callback,
		    SocketDescriptor _fd=SocketDescriptor::Undefined()) noexcept
		:loop(_loop),
		 callback(_callback),
		 fd(_fd) {}
85

86 87 88
	~SocketEvent() noexcept {
		Cancel();
	}
89

90 91 92
	SocketEvent(const SocketEvent &) = delete;
	SocketEvent &operator=(const SocketEvent &) = delete;

93
	auto &GetEventLoop() const noexcept {
94 95 96
		return loop;
	}

97
	bool IsDefined() const noexcept {
98
		return fd.IsDefined();
99 100
	}

101
	SocketDescriptor GetSocket() const noexcept {
102 103 104
		return fd;
	}

105 106 107 108
	SocketDescriptor ReleaseSocket() noexcept {
		Cancel();
		return std::exchange(fd, SocketDescriptor::Undefined());
	}
109

110
	void Open(SocketDescriptor fd) noexcept;
111

112 113 114
	/**
	 * Close the socket (and cancel all scheduled events).
	 */
115
	void Close() noexcept;
116

117 118 119 120 121 122 123 124 125 126 127 128
	/**
	 * Call this instead of Cancel() to unregister this object
	 * after the underlying socket has already been closed.  This
	 * skips the `EPOLL_CTL_DEL` call because the kernel
	 * automatically removes closed file descriptors from epoll.
	 *
	 * Doing `EPOLL_CTL_DEL` on a closed file descriptor usually
	 * fails with `-EBADF` or could unregister a different socket
	 * which happens to be on the same file descriptor number.
	 */
	void Abandon() noexcept;

129
	unsigned GetScheduledFlags() const noexcept {
130
		return scheduled_flags;
131 132
	}

133 134 135 136
	void SetReadyFlags(unsigned flags) noexcept {
		ready_flags = flags;
	}

137 138 139 140 141
	/**
	 * @return true on success, false on error (with errno set if
	 * USE_EPOLL is defined)
	 */
	bool Schedule(unsigned flags) noexcept;
142

143
	void Cancel() noexcept {
144
		Schedule(0);
145 146
	}

147
	bool ScheduleRead() noexcept {
148
		return Schedule(GetScheduledFlags() | READ);
149 150
	}

151 152
	bool ScheduleWrite() noexcept {
		return Schedule(GetScheduledFlags() | WRITE);
153 154
	}

155
	void CancelRead() noexcept {
156
		Schedule(GetScheduledFlags() & ~READ);
157 158
	}

159
	void CancelWrite() noexcept {
160
		Schedule(GetScheduledFlags() & ~WRITE);
161 162
	}

163 164 165
	/**
	 * Schedule only the #IMPLICIT_FLAGS without #READ and #WRITE.
	 */
166 167 168
	void ScheduleImplicit() noexcept {
		Schedule(IMPLICIT_FLAGS);
	}
169 170 171 172 173 174 175 176 177

	bool IsReadPending() const noexcept {
		return GetScheduledFlags() & READ;
	}

	bool IsWritePending() const noexcept {
		return GetScheduledFlags() & WRITE;
	}

178
private:
179 180 181
	/**
	 * Dispatch the events that were passed to SetReadyFlags().
	 */
182
	void Dispatch() noexcept;
183 184 185
};

#endif