xref: /haiku/headers/private/net/ProtocolUtilities.h (revision 73ad2473e7874b3702cf5b0fdf4c81b747812ed9)
1 /*
2  * Copyright 2007-2013, 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 
41 class NetModuleBundleGetter {
42 public:
43 	static net_stack_module_info* Stack() { return gStackModule; }
44 	static net_buffer_module_info* Buffer() { return gBufferModule; }
45 };
46 
47 
48 class ProtocolSocket {
49 public:
50 								ProtocolSocket(net_socket* socket);
51 
52 			status_t			Open();
53 
54 			SocketAddress		LocalAddress()
55 									{ return SocketAddress(
56 										fDomain->address_module,
57 										&fSocket->address); }
58 			ConstSocketAddress	LocalAddress() const
59 									{ return ConstSocketAddress(
60 										fDomain->address_module,
61 										&fSocket->address); }
62 
63 			SocketAddress		PeerAddress()
64 									{ return SocketAddress(
65 										fDomain->address_module,
66 										&fSocket->peer); }
67 			ConstSocketAddress	PeerAddress() const
68 									{ return ConstSocketAddress(
69 										fDomain->address_module,
70 										&fSocket->peer); }
71 
72 			net_domain*			Domain() const { return fDomain; }
73 			net_address_module_info* AddressModule() const
74 									{ return fDomain->address_module; }
75 
76 			net_socket*			Socket() const { return fSocket; }
77 
78 protected:
79 			net_socket*			fSocket;
80 			net_domain*			fDomain;
81 };
82 
83 
84 inline
85 ProtocolSocket::ProtocolSocket(net_socket* socket)
86 	:
87 	fSocket(socket),
88 	fDomain(NULL)
89 {
90 }
91 
92 
93 inline status_t
94 ProtocolSocket::Open()
95 {
96 	fDomain = fSocket->first_protocol->module->get_domain(
97 		fSocket->first_protocol);
98 
99 	if (fDomain == NULL || fDomain->address_module == NULL)
100 		return EAFNOSUPPORT;
101 
102 	return B_OK;
103 }
104 
105 
106 template<typename LockingBase = MutexLocking,
107 	typename ModuleBundle = NetModuleBundleGetter>
108 class DatagramSocket : public ProtocolSocket {
109 public:
110 								DatagramSocket(const char* name,
111 									net_socket* socket);
112 	virtual						~DatagramSocket();
113 
114 			status_t			InitCheck() const;
115 
116 			status_t			Enqueue(net_buffer* buffer);
117 			status_t			EnqueueClone(net_buffer* buffer);
118 
119 			status_t			Dequeue(uint32 flags, net_buffer** _buffer);
120 			net_buffer*			Dequeue(bool clone);
121 			status_t			BlockingDequeue(bool peek, bigtime_t timeout,
122 									net_buffer** _buffer);
123 
124 			void				Clear();
125 
126 			bool				IsEmpty() const { return fBuffers.IsEmpty(); }
127 			ssize_t				AvailableData() const;
128 
129 			void				WakeAll();
130 			void				NotifyOne();
131 
132 protected:
133 	virtual	status_t			SocketStatus(bool peek) const;
134 
135 private:
136 			status_t			_Enqueue(net_buffer* buffer);
137 			net_buffer*			_Dequeue(bool peek);
138 			void				_Clear();
139 
140 			status_t			_Wait(bigtime_t timeout);
141 			void				_NotifyOneReader(bool notifySocket);
142 
143 			bigtime_t			_SocketTimeout(uint32 flags) const;
144 
145 protected:
146 	typedef typename LockingBase::Type LockType;
147 	typedef typename LockingBase::AutoLocker AutoLocker;
148 	typedef DoublyLinkedListCLink<net_buffer> NetBufferLink;
149 	typedef DoublyLinkedList<net_buffer, NetBufferLink> BufferList;
150 
151 			sem_id				fNotify;
152 			BufferList			fBuffers;
153 			size_t				fCurrentBytes;
154 	mutable	LockType			fLock;
155 };
156 
157 
158 #define DECL_DATAGRAM_SOCKET(args) \
159 	template<typename LockingBase, typename ModuleBundle> args \
160 	DatagramSocket<LockingBase, ModuleBundle>
161 
162 
163 DECL_DATAGRAM_SOCKET(inline)::DatagramSocket(const char* name,
164 	net_socket* socket)
165 	:
166 	ProtocolSocket(socket), fCurrentBytes(0)
167 {
168 	status_t status = LockingBase::Init(&fLock, name);
169 	if (status != B_OK)
170 		fNotify = status;
171 	else
172 		fNotify = create_sem(0, name);
173 }
174 
175 
176 DECL_DATAGRAM_SOCKET(inline)::~DatagramSocket()
177 {
178 	_Clear();
179 	delete_sem(fNotify);
180 	LockingBase::Destroy(&fLock);
181 }
182 
183 
184 DECL_DATAGRAM_SOCKET(inline status_t)::InitCheck() const
185 {
186 	return fNotify >= 0 ? B_OK : fNotify;
187 }
188 
189 
190 DECL_DATAGRAM_SOCKET(inline status_t)::Enqueue(net_buffer* buffer)
191 {
192 	AutoLocker _(fLock);
193 	return _Enqueue(buffer);
194 }
195 
196 
197 DECL_DATAGRAM_SOCKET(inline status_t)::EnqueueClone(net_buffer* _buffer)
198 {
199 	AutoLocker _(fLock);
200 
201 	net_buffer* buffer = ModuleBundle::Buffer()->clone(_buffer, false);
202 	if (buffer == NULL)
203 		return B_NO_MEMORY;
204 
205 	status_t status = _Enqueue(buffer);
206 	if (status != B_OK)
207 		ModuleBundle::Buffer()->free(buffer);
208 
209 	return status;
210 }
211 
212 
213 DECL_DATAGRAM_SOCKET(inline status_t)::Dequeue(uint32 flags,
214 	net_buffer** _buffer)
215 {
216 	return BlockingDequeue((flags & MSG_PEEK) != 0, _SocketTimeout(flags),
217 		_buffer);
218 }
219 
220 
221 DECL_DATAGRAM_SOCKET(inline net_buffer*)::Dequeue(bool peek)
222 {
223 	AutoLocker _(fLock);
224 	return _Dequeue(peek);
225 }
226 
227 
228 DECL_DATAGRAM_SOCKET(inline status_t)::BlockingDequeue(bool peek,
229 	bigtime_t timeout, net_buffer** _buffer)
230 {
231 	AutoLocker _(fLock);
232 
233 	bool waited = false;
234 	while (fBuffers.IsEmpty()) {
235 		status_t status = SocketStatus(peek);
236 		if (status != B_OK) {
237 			if (peek)
238 				_NotifyOneReader(false);
239 			return status;
240 		}
241 
242 		status = _Wait(timeout);
243 		if (status != B_OK)
244 			return status;
245 
246 		waited = true;
247 	}
248 
249 	*_buffer = _Dequeue(peek);
250 	if (peek && waited) {
251 		// There is a new buffer in the list; but since we are only peeking,
252 		// notify the next waiting reader.
253 		_NotifyOneReader(false);
254 	}
255 
256 	if (*_buffer == NULL)
257 		return B_NO_MEMORY;
258 
259 	return B_OK;
260 }
261 
262 
263 DECL_DATAGRAM_SOCKET(inline void)::Clear()
264 {
265 	AutoLocker _(fLock);
266 	_Clear();
267 }
268 
269 
270 DECL_DATAGRAM_SOCKET(inline ssize_t)::AvailableData() const
271 {
272 	AutoLocker _(fLock);
273 	status_t status = SocketStatus(true);
274 	if (status < B_OK)
275 		return status;
276 
277 	return fCurrentBytes;
278 }
279 
280 
281 DECL_DATAGRAM_SOCKET(inline void)::WakeAll()
282 {
283 	release_sem_etc(fNotify, 0, B_RELEASE_ALL);
284 }
285 
286 
287 DECL_DATAGRAM_SOCKET(inline void)::NotifyOne()
288 {
289 	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
290 		| B_DO_NOT_RESCHEDULE);
291 }
292 
293 
294 DECL_DATAGRAM_SOCKET(inline status_t)::SocketStatus(bool peek) const
295 {
296 	if (peek)
297 		return fSocket->error;
298 
299 	status_t status = fSocket->error;
300 	fSocket->error = B_OK;
301 
302 	return status;
303 }
304 
305 
306 DECL_DATAGRAM_SOCKET(inline status_t)::_Enqueue(net_buffer* buffer)
307 {
308 	if (fSocket->receive.buffer_size > 0
309 		&& (fCurrentBytes + buffer->size) > fSocket->receive.buffer_size)
310 		return ENOBUFS;
311 
312 	fBuffers.Add(buffer);
313 	fCurrentBytes += buffer->size;
314 
315 	_NotifyOneReader(true);
316 
317 	return B_OK;
318 }
319 
320 
321 DECL_DATAGRAM_SOCKET(inline net_buffer*)::_Dequeue(bool peek)
322 {
323 	if (fBuffers.IsEmpty())
324 		return NULL;
325 
326 	if (peek)
327 		return ModuleBundle::Buffer()->clone(fBuffers.Head(), false);
328 
329 	net_buffer* buffer = fBuffers.RemoveHead();
330 	fCurrentBytes -= buffer->size;
331 
332 	return buffer;
333 }
334 
335 
336 DECL_DATAGRAM_SOCKET(inline void)::_Clear()
337 {
338 	BufferList::Iterator it = fBuffers.GetIterator();
339 	while (it.HasNext())
340 		ModuleBundle::Buffer()->free(it.Next());
341 	fCurrentBytes = 0;
342 }
343 
344 
345 DECL_DATAGRAM_SOCKET(inline status_t)::_Wait(bigtime_t timeout)
346 {
347 	LockingBase::Unlock(&fLock);
348 	status_t status = acquire_sem_etc(fNotify, 1, B_CAN_INTERRUPT
349 		| (timeout != 0 ? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT), timeout);
350 	LockingBase::Lock(&fLock);
351 
352 	return status;
353 }
354 
355 
356 DECL_DATAGRAM_SOCKET(inline void)::_NotifyOneReader(bool notifySocket)
357 {
358 	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
359 		| B_DO_NOT_RESCHEDULE);
360 
361 	if (notifySocket) {
362 		ModuleBundle::Stack()->notify_socket(fSocket, B_SELECT_READ,
363 			fCurrentBytes);
364 	}
365 }
366 
367 
368 DECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
369 {
370 	if (ModuleBundle::Stack()->is_restarted_syscall())
371 		return ModuleBundle::Stack()->restore_syscall_restart_timeout();
372 
373 	bigtime_t timeout = fSocket->receive.timeout;
374 	if ((flags & MSG_DONTWAIT) != 0)
375 		timeout = 0;
376 	else if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
377 		timeout += system_time();
378 
379 	ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
380 	return timeout;
381 }
382 
383 
384 #endif	// PROTOCOL_UTILITIES_H
385