xref: /haiku/headers/private/net/ProtocolUtilities.h (revision a4ef4a49150f118d47324242917a596a3f8f8bd5)
1 /*
2  * Copyright 2007, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *      Hugo Santos, hugosantos@gmail.com
7  */
8 #ifndef PROTOCOL_UTILITIES_H
9 #define PROTOCOL_UTILITIES_H
10 
11 
12 #include <lock.h>
13 #include <Select.h>
14 #include <util/AutoLock.h>
15 #include <util/DoublyLinkedList.h>
16 
17 #include <AddressUtilities.h>
18 #include <net_buffer.h>
19 #include <net_protocol.h>
20 #include <net_socket.h>
21 #include <net_stack.h>
22 
23 
24 class MutexLocking {
25 public:
26 	typedef mutex Type;
27 	typedef MutexLocker AutoLocker;
28 
29 	static status_t Init(mutex *lock, const char *name)
30 		{ mutex_init_etc(lock, name, MUTEX_FLAG_CLONE_NAME); return B_OK; }
31 	static void Destroy(mutex *lock) { mutex_destroy(lock); }
32 	static status_t Lock(mutex *lock) { return mutex_lock(lock); }
33 	static status_t Unlock(mutex *lock) { mutex_unlock(lock); return B_OK; }
34 };
35 
36 
37 extern net_buffer_module_info *gBufferModule;
38 extern net_stack_module_info *gStackModule;
39 
40 class NetModuleBundleGetter {
41 public:
42 	static net_stack_module_info *Stack() { return gStackModule; }
43 	static net_buffer_module_info *Buffer() { return gBufferModule; }
44 };
45 
46 
47 class ProtocolSocket {
48 public:
49 	ProtocolSocket(net_socket *socket);
50 
51 	status_t Open();
52 
53 	SocketAddress LocalAddress()
54 		{ return SocketAddress(fDomain->address_module, &fSocket->address); }
55 	ConstSocketAddress LocalAddress() const
56 		{ return ConstSocketAddress(fDomain->address_module, &fSocket->address); }
57 
58 	SocketAddress PeerAddress()
59 		{ return SocketAddress(fDomain->address_module, &fSocket->peer); }
60 	ConstSocketAddress PeerAddress() const
61 		{ return ConstSocketAddress(fDomain->address_module, &fSocket->peer); }
62 
63 	net_domain *Domain() const { return fDomain; }
64 	net_address_module_info *AddressModule() const
65 		{ return fDomain->address_module; }
66 
67 protected:
68 	net_socket *fSocket;
69 	net_domain *fDomain;
70 };
71 
72 
73 inline ProtocolSocket::ProtocolSocket(net_socket *socket)
74 	: fSocket(socket), fDomain(NULL) {}
75 
76 
77 inline status_t
78 ProtocolSocket::Open()
79 {
80 	fDomain = fSocket->first_protocol->module->get_domain(
81 		fSocket->first_protocol);
82 
83 	if (fDomain == NULL || fDomain->address_module == NULL)
84 		return EAFNOSUPPORT;
85 
86 	return B_OK;
87 }
88 
89 
90 template<typename LockingBase = MutexLocking,
91 	typename ModuleBundle = NetModuleBundleGetter>
92 class DatagramSocket : public ProtocolSocket {
93 public:
94 	DatagramSocket(const char *name, net_socket *socket);
95 	virtual ~DatagramSocket();
96 
97 	status_t InitCheck() const;
98 
99 	status_t Enqueue(net_buffer *buffer);
100 	net_buffer *Dequeue(bool clone);
101 	status_t BlockingDequeue(bool clone, bigtime_t timeout,
102 		net_buffer **_buffer);
103 	void Clear();
104 
105 	status_t SocketEnqueue(net_buffer *buffer);
106 	status_t SocketDequeue(uint32 flags, net_buffer **_buffer);
107 
108 	ssize_t AvailableData() const;
109 
110 	void WakeAll();
111 
112 	net_socket *Socket() const { return fSocket; }
113 
114 protected:
115 	virtual status_t _SocketStatus() const;
116 
117 	status_t _Enqueue(net_buffer *buffer);
118 	status_t _SocketEnqueue(net_buffer *buffer);
119 	net_buffer *_Dequeue(bool clone);
120 	void _Clear();
121 
122 	status_t _Wait(bigtime_t timeout);
123 	void _NotifyOneReader(bool notifySocket);
124 
125 	bool _IsEmpty() const { return fBuffers.IsEmpty(); }
126 	bigtime_t _SocketTimeout(uint32 flags) const;
127 
128 	typedef typename LockingBase::Type LockType;
129 	typedef typename LockingBase::AutoLocker AutoLocker;
130 	typedef DoublyLinkedListCLink<net_buffer> NetBufferLink;
131 	typedef DoublyLinkedList<net_buffer, NetBufferLink> BufferList;
132 
133 	sem_id fNotify;
134 	BufferList fBuffers;
135 	size_t fCurrentBytes;
136 	mutable LockType fLock;
137 };
138 
139 
140 #define DECL_DATAGRAM_SOCKET(args) \
141 	template<typename LockingBase, typename ModuleBundle> args \
142 	DatagramSocket<LockingBase, ModuleBundle>
143 
144 
145 DECL_DATAGRAM_SOCKET(inline)::DatagramSocket(const char *name,
146 	net_socket *socket)
147 	: ProtocolSocket(socket), fCurrentBytes(0)
148 {
149 	status_t status = LockingBase::Init(&fLock, name);
150 	if (status < B_OK)
151 		fNotify = status;
152 	else
153 		fNotify = create_sem(0, name);
154 }
155 
156 
157 DECL_DATAGRAM_SOCKET(inline)::~DatagramSocket()
158 {
159 	_Clear();
160 	delete_sem(fNotify);
161 	LockingBase::Destroy(&fLock);
162 }
163 
164 
165 DECL_DATAGRAM_SOCKET(inline status_t)::InitCheck() const
166 {
167 	return fNotify;
168 }
169 
170 
171 DECL_DATAGRAM_SOCKET(inline status_t)::Enqueue(net_buffer *buffer)
172 {
173 	AutoLocker _(fLock);
174 	return _Enqueue(buffer);
175 }
176 
177 
178 DECL_DATAGRAM_SOCKET(inline status_t)::_Enqueue(net_buffer *buffer)
179 {
180 	if (fSocket->receive.buffer_size > 0
181 		&& (fCurrentBytes + buffer->size) > fSocket->receive.buffer_size)
182 		return ENOBUFS;
183 
184 	fBuffers.Add(buffer);
185 	fCurrentBytes += buffer->size;
186 
187 	_NotifyOneReader(true);
188 
189 	return B_OK;
190 }
191 
192 
193 DECL_DATAGRAM_SOCKET(inline status_t)::SocketEnqueue(net_buffer *_buffer)
194 {
195 	AutoLocker _(fLock);
196 	return _SocketEnqueue(_buffer);
197 }
198 
199 
200 DECL_DATAGRAM_SOCKET(inline status_t)::_SocketEnqueue(net_buffer *_buffer)
201 {
202 	net_buffer *buffer = ModuleBundle::Buffer()->clone(_buffer, false);
203 	if (buffer == NULL)
204 		return B_NO_MEMORY;
205 
206 	status_t status = _Enqueue(buffer);
207 	if (status < B_OK)
208 		ModuleBundle::Buffer()->free(buffer);
209 
210 	return status;
211 }
212 
213 
214 DECL_DATAGRAM_SOCKET(inline net_buffer *)::Dequeue(bool clone)
215 {
216 	AutoLocker _(fLock);
217 	return _Dequeue(clone);
218 }
219 
220 
221 DECL_DATAGRAM_SOCKET(inline net_buffer *)::_Dequeue(bool clone)
222 {
223 	if (fBuffers.IsEmpty())
224 		return NULL;
225 
226 	if (clone)
227 		return ModuleBundle::Buffer()->clone(fBuffers.Head(), false);
228 
229 	net_buffer *buffer = fBuffers.RemoveHead();
230 	fCurrentBytes -= buffer->size;
231 
232 	return buffer;
233 }
234 
235 
236 DECL_DATAGRAM_SOCKET(inline status_t)::BlockingDequeue(bool clone,
237 	bigtime_t timeout, net_buffer **_buffer)
238 {
239 	AutoLocker _(fLock);
240 
241 	bool waited = false;
242 	while (fBuffers.IsEmpty()) {
243 		status_t status = _SocketStatus();
244 		if (status < B_OK)
245 			return status;
246 
247 		if ((status = _Wait(timeout)) < B_OK)
248 			return status;
249 		waited = true;
250 	}
251 
252 	*_buffer = _Dequeue(clone);
253 	if (clone && waited) {
254 		// we were signalled there was a new buffer in the
255 		// list; but since we are cloning, notify the next
256 		// waiting reader.
257 		_NotifyOneReader(false);
258 	}
259 
260 	if (*_buffer == NULL)
261 		return B_NO_MEMORY;
262 
263 	return B_OK;
264 }
265 
266 
267 DECL_DATAGRAM_SOCKET(inline status_t)::SocketDequeue(uint32 flags,
268 	net_buffer **_buffer)
269 {
270 	return BlockingDequeue(flags & MSG_PEEK, _SocketTimeout(flags), _buffer);
271 }
272 
273 
274 DECL_DATAGRAM_SOCKET(inline void)::Clear()
275 {
276 	AutoLocker _(fLock);
277 	_Clear();
278 }
279 
280 
281 DECL_DATAGRAM_SOCKET(inline void)::_Clear()
282 {
283 	BufferList::Iterator it = fBuffers.GetIterator();
284 	while (it.HasNext())
285 		ModuleBundle::Buffer()->free(it.Next());
286 	fCurrentBytes = 0;
287 }
288 
289 
290 DECL_DATAGRAM_SOCKET(inline ssize_t)::AvailableData() const
291 {
292 	AutoLocker _(fLock);
293 	status_t status = _SocketStatus();
294 	if (status < B_OK)
295 		return status;
296 
297 	return fCurrentBytes;
298 }
299 
300 
301 DECL_DATAGRAM_SOCKET(inline status_t)::_SocketStatus() const
302 {
303 	return B_OK;
304 }
305 
306 
307 DECL_DATAGRAM_SOCKET(inline status_t)::_Wait(bigtime_t timeout)
308 {
309 	LockingBase::Unlock(&fLock);
310 	status_t status = acquire_sem_etc(fNotify, 1, B_CAN_INTERRUPT
311 		| B_ABSOLUTE_TIMEOUT, timeout);
312 	LockingBase::Lock(&fLock);
313 
314 	return status;
315 }
316 
317 
318 DECL_DATAGRAM_SOCKET(inline void)::WakeAll()
319 {
320 	release_sem_etc(fNotify, 0, B_RELEASE_ALL);
321 }
322 
323 
324 DECL_DATAGRAM_SOCKET(inline void)::_NotifyOneReader(bool notifySocket)
325 {
326 	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
327 		| B_DO_NOT_RESCHEDULE);
328 
329 	if (notifySocket)
330 		ModuleBundle::Stack()->notify_socket(fSocket, B_SELECT_READ,
331 			fCurrentBytes);
332 }
333 
334 
335 DECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
336 {
337 	bigtime_t timeout = fSocket->receive.timeout;
338 
339 	if (flags & MSG_DONTWAIT)
340 		timeout = 0;
341 	else if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
342 		timeout += system_time();
343 
344 	if (ModuleBundle::Stack()->is_restarted_syscall())
345 		timeout = ModuleBundle::Stack()->restore_syscall_restart_timeout();
346 	else
347 		ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
348 
349 	return timeout;
350 }
351 
352 #endif
353