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