xref: /haiku/headers/private/net/ProtocolUtilities.h (revision 3634f142352af2428aed187781fc9d75075e9140)
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 	 if ((flags & ~(MSG_DONTWAIT | MSG_PEEK)) != 0)
217 		return EOPNOTSUPP;
218 
219 	return BlockingDequeue((flags & MSG_PEEK) != 0, _SocketTimeout(flags),
220 		_buffer);
221 }
222 
223 
224 DECL_DATAGRAM_SOCKET(inline net_buffer*)::Dequeue(bool peek)
225 {
226 	AutoLocker _(fLock);
227 	return _Dequeue(peek);
228 }
229 
230 
231 DECL_DATAGRAM_SOCKET(inline status_t)::BlockingDequeue(bool peek,
232 	bigtime_t timeout, net_buffer** _buffer)
233 {
234 	AutoLocker _(fLock);
235 
236 	bool waited = false;
237 	while (fBuffers.IsEmpty()) {
238 		status_t status = SocketStatus(peek);
239 		if (status != B_OK) {
240 			if (peek)
241 				_NotifyOneReader(false);
242 			return status;
243 		}
244 
245 		status = _Wait(timeout);
246 		if (status != B_OK)
247 			return status;
248 
249 		waited = true;
250 	}
251 
252 	*_buffer = _Dequeue(peek);
253 	if (peek && waited) {
254 		// There is a new buffer in the list; but since we are only peeking,
255 		// notify the next waiting reader.
256 		_NotifyOneReader(false);
257 	}
258 
259 	if (*_buffer == NULL)
260 		return B_NO_MEMORY;
261 
262 	return B_OK;
263 }
264 
265 
266 DECL_DATAGRAM_SOCKET(inline void)::Clear()
267 {
268 	AutoLocker _(fLock);
269 	_Clear();
270 }
271 
272 
273 DECL_DATAGRAM_SOCKET(inline ssize_t)::AvailableData() const
274 {
275 	AutoLocker _(fLock);
276 	status_t status = SocketStatus(true);
277 	if (status < B_OK)
278 		return status;
279 
280 	return fCurrentBytes;
281 }
282 
283 
284 DECL_DATAGRAM_SOCKET(inline void)::WakeAll()
285 {
286 	release_sem_etc(fNotify, 0, B_RELEASE_ALL);
287 }
288 
289 
290 DECL_DATAGRAM_SOCKET(inline void)::NotifyOne()
291 {
292 	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
293 		| B_DO_NOT_RESCHEDULE);
294 }
295 
296 
297 DECL_DATAGRAM_SOCKET(inline status_t)::SocketStatus(bool peek) const
298 {
299 	if (peek)
300 		return fSocket->error;
301 
302 	status_t status = fSocket->error;
303 	fSocket->error = B_OK;
304 
305 	return status;
306 }
307 
308 
309 DECL_DATAGRAM_SOCKET(inline status_t)::_Enqueue(net_buffer* buffer)
310 {
311 	if (fSocket->receive.buffer_size > 0
312 		&& (fCurrentBytes + buffer->size) > fSocket->receive.buffer_size)
313 		return ENOBUFS;
314 
315 	fBuffers.Add(buffer);
316 	fCurrentBytes += buffer->size;
317 
318 	_NotifyOneReader(true);
319 
320 	return B_OK;
321 }
322 
323 
324 DECL_DATAGRAM_SOCKET(inline net_buffer*)::_Dequeue(bool peek)
325 {
326 	if (fBuffers.IsEmpty())
327 		return NULL;
328 
329 	if (peek)
330 		return ModuleBundle::Buffer()->clone(fBuffers.Head(), false);
331 
332 	net_buffer* buffer = fBuffers.RemoveHead();
333 	fCurrentBytes -= buffer->size;
334 
335 	return buffer;
336 }
337 
338 
339 DECL_DATAGRAM_SOCKET(inline void)::_Clear()
340 {
341 	BufferList::Iterator it = fBuffers.GetIterator();
342 	while (it.HasNext())
343 		ModuleBundle::Buffer()->free(it.Next());
344 	fCurrentBytes = 0;
345 }
346 
347 
348 DECL_DATAGRAM_SOCKET(inline status_t)::_Wait(bigtime_t timeout)
349 {
350 	LockingBase::Unlock(&fLock);
351 	status_t status = acquire_sem_etc(fNotify, 1, B_CAN_INTERRUPT
352 		| (timeout != 0 ? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT), timeout);
353 	LockingBase::Lock(&fLock);
354 
355 	return status;
356 }
357 
358 
359 DECL_DATAGRAM_SOCKET(inline void)::_NotifyOneReader(bool notifySocket)
360 {
361 	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
362 		| B_DO_NOT_RESCHEDULE);
363 
364 	if (notifySocket) {
365 		ModuleBundle::Stack()->notify_socket(fSocket, B_SELECT_READ,
366 			fCurrentBytes);
367 	}
368 }
369 
370 
371 DECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
372 {
373 	if ((flags & MSG_DONTWAIT) != 0)
374 		return 0;
375 	if (ModuleBundle::Stack()->is_restarted_syscall())
376 		return ModuleBundle::Stack()->restore_syscall_restart_timeout();
377 
378 	bigtime_t timeout = fSocket->receive.timeout;
379 	if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
380 		timeout += system_time();
381 
382 	ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
383 	return timeout;
384 }
385 
386 
387 #endif	// PROTOCOL_UTILITIES_H
388