BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / network / stack / utility.cpp
blob55cf0dcc973abf6fe155bdb589ee9a1cb7adcf73
1 /*
2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 */
10 #include "utility.h"
12 #include <ByteOrder.h>
13 #include <KernelExport.h>
15 #include <condition_variable.h>
16 #include <net_buffer.h>
17 #include <syscall_restart.h>
18 #include <util/AutoLock.h>
20 #include "stack_private.h"
23 //#define TRACE_UTILITY
24 #ifdef TRACE_UTILITY
25 # define TRACE(x...) dprintf(x)
26 #else
27 # define TRACE(x...) ;
28 #endif
31 // internal Fifo class which doesn't maintain it's own lock
32 // TODO: do we need this one for anything?
33 class Fifo {
34 public:
35 Fifo(const char* name, size_t maxBytes);
36 ~Fifo();
38 status_t InitCheck() const;
40 status_t Enqueue(net_buffer* buffer);
41 status_t EnqueueAndNotify(net_buffer* _buffer, net_socket* socket,
42 uint8 event);
43 status_t Wait(mutex* lock, bigtime_t timeout);
44 net_buffer* Dequeue(bool clone);
45 status_t Clear();
47 void WakeAll();
49 bool IsEmpty() const { return current_bytes == 0; }
51 //private:
52 // these field names are kept so we can use templatized
53 // functions together with net_fifo
54 sem_id notify;
55 int32 waiting;
56 size_t max_bytes;
57 size_t current_bytes;
58 struct list buffers;
63 static struct list sTimers;
64 static mutex sTimerLock;
65 static sem_id sTimerWaitSem;
66 static ConditionVariable sWaitForTimerCondition;
67 static net_timer* sCurrentTimer;
68 static thread_id sTimerThread;
69 static bigtime_t sTimerTimeout;
72 static inline void
73 fifo_notify_one_reader(int32& waiting, sem_id sem)
75 if (waiting > 0) {
76 waiting--;
77 release_sem_etc(sem, 1, B_DO_NOT_RESCHEDULE);
82 template<typename FifoType> static inline status_t
83 base_fifo_init(FifoType* fifo, const char* name, size_t maxBytes)
85 fifo->notify = create_sem(0, name);
86 fifo->max_bytes = maxBytes;
87 fifo->current_bytes = 0;
88 fifo->waiting = 0;
89 list_init(&fifo->buffers);
91 return fifo->notify;
95 template<typename FifoType> static inline status_t
96 base_fifo_enqueue_buffer(FifoType* fifo, net_buffer* buffer)
98 if (fifo->max_bytes > 0
99 && fifo->current_bytes + buffer->size > fifo->max_bytes)
100 return ENOBUFS;
102 list_add_item(&fifo->buffers, buffer);
103 fifo->current_bytes += buffer->size;
104 fifo_notify_one_reader(fifo->waiting, fifo->notify);
106 return B_OK;
110 template<typename FifoType> static inline status_t
111 base_fifo_clear(FifoType* fifo)
113 while (true) {
114 net_buffer* buffer = (net_buffer*)list_remove_head_item(&fifo->buffers);
115 if (buffer == NULL)
116 break;
118 gNetBufferModule.free(buffer);
121 fifo->current_bytes = 0;
122 return B_OK;
126 // #pragma mark - UserBuffer
129 void*
130 UserBuffer::Push(void* source, size_t length)
132 if (fStatus != B_OK)
133 return NULL;
135 if (fAvailable < length) {
136 fStatus = ENOBUFS;
137 return NULL;
140 #ifdef _KERNEL_MODE
141 fStatus = user_memcpy(fBuffer, source, length);
142 if (fStatus != B_OK)
143 return NULL;
144 #else
145 memcpy(fBuffer, source, length);
146 #endif
148 void* current = fBuffer;
150 fAvailable -= length;
151 fBuffer += length;
153 return current;
157 status_t
158 UserBuffer::Pad(size_t length)
160 if (fStatus != B_OK)
161 return fStatus;
163 if (fAvailable < length)
164 return fStatus = ENOBUFS;
166 fStatus = user_memset(fBuffer, 0, length);
167 if (fStatus != B_OK)
168 return fStatus;
170 fAvailable -= length;
171 fBuffer += length;
173 return B_OK;
177 status_t
178 UserBuffer::PadToNext(size_t length)
180 return Pad((BytesConsumed() + length - 1) / length - BytesConsumed());
184 // #pragma mark -
187 uint16
188 compute_checksum(uint8* _buffer, size_t length)
190 uint16* buffer = (uint16*)_buffer;
191 uint32 sum = 0;
193 // TODO: unfold loop for speed
194 // TODO: write processor dependent version for speed
195 while (length >= 2) {
196 sum += *buffer++;
197 length -= 2;
200 if (length) {
201 // give the last byte it's proper endian-aware treatment
202 #if B_HOST_IS_LENDIAN
203 sum += *(uint8*)buffer;
204 #else
205 uint8 ordered[2];
206 ordered[0] = *(uint8*)buffer;
207 ordered[1] = 0;
208 sum += *(uint16*)ordered;
209 #endif
212 while (sum >> 16) {
213 sum = (sum & 0xffff) + (sum >> 16);
216 return sum;
220 uint16
221 checksum(uint8* buffer, size_t length)
223 return ~compute_checksum(buffer, length);
227 // #pragma mark - Notifications
230 status_t
231 notify_socket(net_socket* socket, uint8 event, int32 value)
233 return gNetSocketModule.notify(socket, event, value);
237 // #pragma mark - FIFOs
240 Fifo::Fifo(const char* name, size_t maxBytes)
242 base_fifo_init(this, name, maxBytes);
246 Fifo::~Fifo()
248 Clear();
249 delete_sem(notify);
253 status_t
254 Fifo::InitCheck() const
256 return !(notify < B_OK);
260 status_t
261 Fifo::Enqueue(net_buffer* buffer)
263 return base_fifo_enqueue_buffer(this, buffer);
267 status_t
268 Fifo::EnqueueAndNotify(net_buffer* _buffer, net_socket* socket, uint8 event)
270 net_buffer *buffer = gNetBufferModule.clone(_buffer, false);
271 if (buffer == NULL)
272 return B_NO_MEMORY;
274 status_t status = Enqueue(buffer);
275 if (status < B_OK)
276 gNetBufferModule.free(buffer);
277 else
278 notify_socket(socket, event, current_bytes);
280 return status;
284 status_t
285 Fifo::Wait(mutex* lock, bigtime_t timeout)
287 waiting++;
288 mutex_unlock(lock);
289 status_t status = acquire_sem_etc(notify, 1,
290 B_CAN_INTERRUPT | B_ABSOLUTE_TIMEOUT, timeout);
291 mutex_lock(lock);
292 return status;
296 net_buffer*
297 Fifo::Dequeue(bool clone)
299 net_buffer* buffer = (net_buffer*)list_get_first_item(&buffers);
301 // assert(buffer != NULL);
303 if (clone) {
304 buffer = gNetBufferModule.clone(buffer, false);
305 fifo_notify_one_reader(waiting, notify);
306 }else {
307 list_remove_item(&buffers, buffer);
308 current_bytes -= buffer->size;
311 return buffer;
315 status_t
316 Fifo::Clear()
318 return base_fifo_clear(this);
322 void
323 Fifo::WakeAll()
325 #ifdef __HAIKU__
326 release_sem_etc(notify, 0, B_RELEASE_ALL);
327 #else
328 release_sem_etc(notify, 0, waiting);
329 #endif
333 status_t
334 init_fifo(net_fifo* fifo, const char* name, size_t maxBytes)
336 mutex_init_etc(&fifo->lock, name, MUTEX_FLAG_CLONE_NAME);
338 status_t status = base_fifo_init(fifo, name, maxBytes);
339 if (status < B_OK)
340 mutex_destroy(&fifo->lock);
342 return status;
346 void
347 uninit_fifo(net_fifo* fifo)
349 clear_fifo(fifo);
351 mutex_destroy(&fifo->lock);
352 delete_sem(fifo->notify);
356 status_t
357 fifo_enqueue_buffer(net_fifo* fifo, net_buffer* buffer)
359 MutexLocker locker(fifo->lock);
360 return base_fifo_enqueue_buffer(fifo, buffer);
364 /*! Gets the first buffer from the FIFO. If there is no buffer, it
365 will wait depending on the \a flags and \a timeout.
366 The following flags are supported (the rest is ignored):
367 MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your
368 socket is O_NONBLOCK, you should specify this flag. A \a timeout of
369 zero is equivalent to this flag, though.
370 MSG_PEEK - returns a clone of the buffer and keep the original
371 in the FIFO.
373 ssize_t
374 fifo_dequeue_buffer(net_fifo* fifo, uint32 flags, bigtime_t timeout,
375 net_buffer** _buffer)
377 MutexLocker locker(fifo->lock);
378 bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0;
379 status_t status;
381 while (true) {
382 net_buffer* buffer = (net_buffer*)list_get_first_item(&fifo->buffers);
383 if (buffer != NULL) {
384 if ((flags & MSG_PEEK) != 0) {
385 // we need to clone the buffer for inspection; we can't give a
386 // handle to a buffer that we're still using
387 buffer = gNetBufferModule.clone(buffer, false);
388 if (buffer == NULL) {
389 status = B_NO_MEMORY;
390 break;
392 } else {
393 list_remove_item(&fifo->buffers, buffer);
394 fifo->current_bytes -= buffer->size;
397 *_buffer = buffer;
398 status = B_OK;
399 break;
402 if (!dontWait)
403 fifo->waiting++;
405 locker.Unlock();
407 if (dontWait)
408 return B_WOULD_BLOCK;
410 // we need to wait until a new buffer becomes available
411 status = acquire_sem_etc(fifo->notify, 1,
412 B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout);
413 if (status < B_OK)
414 return status;
416 locker.Lock();
419 // if another thread is waiting for data, since we didn't
420 // eat the buffer, it will get it
421 if (flags & MSG_PEEK)
422 fifo_notify_one_reader(fifo->waiting, fifo->notify);
424 return status;
428 status_t
429 clear_fifo(net_fifo* fifo)
431 MutexLocker locker(fifo->lock);
432 return base_fifo_clear(fifo);
436 status_t
437 fifo_socket_enqueue_buffer(net_fifo* fifo, net_socket* socket, uint8 event,
438 net_buffer* _buffer)
440 net_buffer *buffer = gNetBufferModule.clone(_buffer, false);
441 if (buffer == NULL)
442 return B_NO_MEMORY;
444 MutexLocker locker(fifo->lock);
446 status_t status = base_fifo_enqueue_buffer(fifo, buffer);
447 if (status < B_OK)
448 gNetBufferModule.free(buffer);
449 else
450 notify_socket(socket, event, fifo->current_bytes);
452 return status;
456 // #pragma mark - Timer
459 static status_t
460 timer_thread(void* /*data*/)
462 status_t status = B_OK;
464 do {
465 bigtime_t timeout = B_INFINITE_TIMEOUT;
467 if (status == B_TIMED_OUT || status == B_OK) {
468 // scan timers for new timeout and/or execute a timer
469 mutex_lock(&sTimerLock);
471 struct net_timer* timer = NULL;
472 while (true) {
473 timer = (net_timer*)list_get_next_item(&sTimers, timer);
474 if (timer == NULL)
475 break;
477 if (timer->due < system_time()) {
478 // execute timer
479 list_remove_item(&sTimers, timer);
480 timer->due = -1;
481 sCurrentTimer = timer;
483 mutex_unlock(&sTimerLock);
484 timer->hook(timer, timer->data);
485 mutex_lock(&sTimerLock);
487 sCurrentTimer = NULL;
488 sWaitForTimerCondition.NotifyAll();
490 timer = NULL;
491 // restart scanning as we unlocked the list
492 } else {
493 // calculate new timeout
494 if (timer->due < timeout)
495 timeout = timer->due;
499 sTimerTimeout = timeout;
500 mutex_unlock(&sTimerLock);
503 status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
504 // the wait sem normally can't be acquired, so we
505 // have to look at the status value the call returns:
507 // B_OK - a new timer has been added or canceled
508 // B_TIMED_OUT - look for timers to be executed
509 // B_BAD_SEM_ID - we are asked to quit
510 } while (status != B_BAD_SEM_ID);
512 return B_OK;
517 Initializes a timer before use. You can also use this function to change
518 a timer later on, but make sure you have canceled it before using set_timer().
520 void
521 init_timer(net_timer* timer, net_timer_func hook, void* data)
523 timer->hook = hook;
524 timer->data = data;
525 timer->due = 0;
526 timer->flags = 0;
530 /*! Sets or cancels a timer. When the \a delay is below zero, an eventually
531 running timer is canceled, if not, it is scheduled to be executed after the
532 specified \a delay.
533 You need to have initialized the timer before calling this function.
535 In case you need to change a running timer, you have to cancel it first,
536 before making any changes.
538 void
539 set_timer(net_timer* timer, bigtime_t delay)
541 MutexLocker locker(sTimerLock);
543 TRACE("set_timer %p, hook %p, data %p\n", timer, timer->hook, timer->data);
545 if (timer->due > 0 && delay < 0) {
546 // this timer is scheduled, cancel it
547 list_remove_item(&sTimers, timer);
548 timer->due = 0;
551 if (delay >= 0) {
552 // reschedule or add this timer
553 if (timer->due <= 0)
554 list_add_item(&sTimers, timer);
556 timer->due = system_time() + delay;
558 // notify timer about the change if necessary
559 if (sTimerTimeout > timer->due)
560 release_sem(sTimerWaitSem);
565 bool
566 cancel_timer(struct net_timer* timer)
568 MutexLocker locker(sTimerLock);
570 TRACE("cancel_timer %p, hook %p, data %p\n", timer, timer->hook,
571 timer->data);
573 if (timer->due <= 0)
574 return false;
576 // this timer is scheduled, cancel it
577 list_remove_item(&sTimers, timer);
578 timer->due = 0;
579 return true;
583 status_t
584 wait_for_timer(struct net_timer* timer)
586 if (find_thread(NULL) == sTimerThread) {
587 // let's not wait for ourselves...
588 return B_BAD_VALUE;
591 while (true) {
592 MutexLocker locker(sTimerLock);
594 if (timer->due <= 0 && sCurrentTimer != timer)
595 return B_OK;
597 // we actually need to wait for this timer
598 ConditionVariableEntry entry;
599 sWaitForTimerCondition.Add(&entry);
601 locker.Unlock();
603 entry.Wait();
606 return B_OK;
610 bool
611 is_timer_active(net_timer* timer)
613 return timer->due > 0;
617 bool
618 is_timer_running(net_timer* timer)
620 return timer == sCurrentTimer;
624 static int
625 dump_timer(int argc, char** argv)
627 kprintf("timer hook data due in\n");
629 struct net_timer* timer = NULL;
630 while (true) {
631 timer = (net_timer*)list_get_next_item(&sTimers, timer);
632 if (timer == NULL)
633 break;
635 kprintf("%p %p %p %" B_PRId64 "\n", timer, timer->hook, timer->data,
636 timer->due > 0 ? timer->due - system_time() : -1);
639 return 0;
643 status_t
644 init_timers(void)
646 list_init(&sTimers);
647 sTimerTimeout = B_INFINITE_TIMEOUT;
649 status_t status = B_OK;
650 mutex_init(&sTimerLock, "net timer");
652 sTimerWaitSem = create_sem(0, "net timer wait");
653 if (sTimerWaitSem < B_OK) {
654 status = sTimerWaitSem;
655 goto err1;
658 sTimerThread = spawn_kernel_thread(timer_thread, "net timer",
659 B_NORMAL_PRIORITY, NULL);
660 if (sTimerThread < B_OK) {
661 status = sTimerThread;
662 goto err2;
665 sWaitForTimerCondition.Init(NULL, "wait for net timer");
667 add_debugger_command("net_timer", dump_timer,
668 "Lists all active network timer");
670 return resume_thread(sTimerThread);
672 err1:
673 mutex_destroy(&sTimerLock);
674 err2:
675 delete_sem(sTimerWaitSem);
676 return status;
680 void
681 uninit_timers(void)
683 delete_sem(sTimerWaitSem);
685 status_t status;
686 wait_for_thread(sTimerThread, &status);
688 mutex_lock(&sTimerLock);
690 mutex_destroy(&sTimerLock);
692 remove_debugger_command("net_timer", dump_timer);
696 // #pragma mark - Syscall restart
699 bool
700 is_syscall(void)
702 return is_called_via_syscall();
706 bool
707 is_restarted_syscall(void)
709 return syscall_restart_is_restarted();
713 void
714 store_syscall_restart_timeout(bigtime_t timeout)
716 Thread* thread = thread_get_current_thread();
717 if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0)
718 *(bigtime_t*)thread->syscall_restart.parameters = timeout;
722 bigtime_t
723 restore_syscall_restart_timeout(void)
725 Thread* thread = thread_get_current_thread();
726 return *(bigtime_t*)thread->syscall_restart.parameters;