1 /*
2  * Copyright 2003-2021 The Music Player Daemon Project
3  * http://www.musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #ifndef EVENT_WINSELECT_BACKEND_HXX
21 #define EVENT_WINSELECT_BACKEND_HXX
22 
23 #include "PollResultGeneric.hxx"
24 
25 #include <algorithm>
26 #include <cassert>
27 #include <unordered_map>
28 
29 #include <winsock2.h>
30 
31 class SocketSet
32 {
33 	fd_set set;
34 public:
SocketSet()35 	SocketSet() noexcept {
36 		set.fd_count = 0;
37 	}
38 
SocketSet(const SocketSet & other)39 	SocketSet(const SocketSet &other) noexcept {
40 		set.fd_count = other.set.fd_count;
41 		std::copy_n(other.set.fd_array, set.fd_count, set.fd_array);
42 	}
43 
GetPtr()44 	fd_set *GetPtr() noexcept {
45 		return IsEmpty() ? nullptr : &set;
46 	}
47 
Size() const48 	size_t Size() const noexcept {
49 		return set.fd_count;
50 	}
51 
IsEmpty() const52 	bool IsEmpty() const noexcept {
53 		return set.fd_count == 0;
54 	}
55 
IsFull() const56 	bool IsFull() const noexcept {
57 		return set.fd_count == FD_SETSIZE;
58 	}
59 
operator [](size_t index) const60 	SOCKET operator[](size_t index) const noexcept {
61 		assert(index < set.fd_count);
62 		return set.fd_array[index];
63 	}
64 
Add(SOCKET fd)65 	size_t Add(SOCKET fd) noexcept {
66 		assert(!IsFull());
67 		set.fd_array[set.fd_count] = fd;
68 		return set.fd_count++;
69 	}
70 
MoveToEnd(size_t index)71 	void MoveToEnd(size_t index) noexcept {
72 		assert(index < set.fd_count);
73 		std::swap(set.fd_array[index], set.fd_array[set.fd_count - 1]);
74 	}
75 
RemoveLast()76 	void RemoveLast() noexcept {
77 		assert(!IsEmpty());
78 		--set.fd_count;
79 	}
80 
begin() const81 	const auto *begin() const noexcept {
82 		return set.fd_array;
83 	}
84 
end() const85 	const auto *end() const noexcept {
86 		return set.fd_array + set.fd_count;
87 	}
88 };
89 
90 class WinSelectBackend
91 {
92 	struct Item
93 	{
94 		int index[2]{-1, -1};
95 		void *obj;
96 		unsigned events = 0;
97 
ItemWinSelectBackend::Item98 		explicit constexpr Item(void *_obj) noexcept
99 			:obj(_obj) {}
100 
101 		Item(const Item &) = delete;
102 		Item &operator=(const Item &) = delete;
103 	};
104 
105 	SocketSet event_set[2];
106 	std::unordered_map<SOCKET, Item> items;
107 
108 public:
109 	WinSelectBackend() noexcept;
110 	~WinSelectBackend() noexcept;
111 
112 	WinSelectBackend(const WinSelectBackend &) = delete;
113 	WinSelectBackend &operator=(const WinSelectBackend &) = delete;
114 
115 	PollResultGeneric ReadEvents(int timeout_ms) noexcept;
116 	bool Add(SOCKET fd, unsigned events, void *obj) noexcept;
117 	bool Modify(SOCKET fd, unsigned events, void *obj) noexcept;
118 	bool Remove(SOCKET fd) noexcept;
Abandon(SOCKET fd)119 	bool Abandon(SOCKET fd) noexcept {
120 		return Remove(fd);
121 	}
122 
123 private:
124 	bool CanModify(Item &item, unsigned events,
125 		       int event_id) const noexcept;
126 	void Modify(Item &item, SOCKET fd, unsigned events,
127 		    int event_id) noexcept;
128 
129 	void ApplyReady(const SocketSet &src, unsigned events) noexcept;
130 };
131 
132 #endif
133