Do not access SysBase->ThisTask outside exec.library. The code should not rely on...
[AROS.git] / workbench / network / stacks / AROSTCP / bsdsocket / kern / kern_synch.c
blobdc8d20ebbbf99064f30f645a1b0b0e0ac28965d8
1 /*
2 * Copyright (C) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
3 * Helsinki University of Technology, Finland.
4 * All rights reserved.
5 * Copyright (C) 2005 - 2007 The AROS Dev Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
19 * MA 02111-1307, USA.
23 #include <conf.h>
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/synch.h>
28 #include <sys/queue2.h>
29 #include <sys/syslog.h>
31 #include <kern/amiga_includes.h>
33 #include <api/amiga_api.h>
35 #include <proto/exec.h>
38 * Note about spl-functions: this implementation does NOT check for software
39 * interrupts when returning to level spl 0 (not needed on AmigaOS, see the
40 * bottom of this file).
44 * Sleeping threads are hashed by 'chan' onto sleep queues.
47 #define SLEEP_QUEUE_SIZE 32 /* power of 2 */
48 #define SLEEP_HASH(x) (((IPTR)(x)>>5) & (SLEEP_QUEUE_SIZE - 1))
50 queue_head_t sleep_queue[SLEEP_QUEUE_SIZE];
53 * semaphore protecting sleep queues
55 struct SignalSemaphore sleep_semaphore;
56 static BOOL sleep_initialized = FALSE;
59 * Sleep system initialization.
61 BOOL
62 sleep_init(void)
64 register int i;
66 #if defined(__AROS__)
67 D(bug("[AROSTCP](kern_synch.c) sleep_init()\n"));
68 #endif
70 if (!sleep_initialized) {
72 * initialize the semaphore protecting sleep queues
74 InitSemaphore(&sleep_semaphore);
77 * initialize the sleep queues
79 for (i = 0; i < SLEEP_QUEUE_SIZE; i++)
80 queue_init(&sleep_queue[i]);
82 sleep_initialized = TRUE;
84 return TRUE;
87 void
88 tsleep_send_timeout(struct SocketBase *p,
89 const struct timeval *time_out)
91 #if defined(__AROS__)
92 D(bug("[AROSTCP](kern_synch.c) tsleep_send_timeout()\n"));
93 #endif
95 * Make sure that the timer message is back from the timer device
97 if (p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type != NT_UNKNOWN) {
99 * abort previous timeout if it has not been completed yet
101 if (p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type != NT_REPLYMSG) {
102 AbortIO((struct IORequest *)(p->tsleep_timer));
105 * Remove timerequest from reply port.
107 WaitIO((struct IORequest *)p->tsleep_timer);
109 * Set the node type to NT_UNKNOWN to mark that it is referenced only by
110 * the p->tsleep_timer.
112 p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;
113 #if 0
115 * Make sure the signal gets cleared.
117 * if this is not done, the tsleep_main will do one unnecessary round
118 * IF the signal was set. IS THIS WORTH OF IT?
120 SetSignal(0, 1 << p->timerPort->mp_SigBit);
121 #endif
124 * send timeout request if necessary
126 if (time_out) {
128 * set the timeout
130 p->tsleep_timer->tr_time = *time_out;
132 * Enable signalling again
134 p->timerPort->mp_Flags = PA_SIGNAL;
136 * send the request to the timer device
138 BeginIO((struct IORequest *)(p->tsleep_timer));
142 void
143 tsleep_abort_timeout(struct SocketBase *p,
144 const struct timeval *time_out)
146 #if defined(__AROS__)
147 D(bug("[AROSTCP](kern_synch.c) tsleep_abort_timeout()\n"));
148 #endif
149 if (time_out) {
150 /* do not signal us any more */
151 p->timerPort->mp_Flags = PA_IGNORE;
155 void
156 tsleep_enter(struct SocketBase *p,
157 caddr_t chan, /* 'channel' to wait on */
158 const char *wmesg) /* reason to sleep */
160 register queue_t q;
161 #if defined(__AROS__)
162 D(bug("[AROSTCP](kern_synch.c) tsleep_enter()\n"));
163 #endif
165 * Zero is a reserved value, used to indicate
166 * that we have been woken up and are no longer on
167 * the sleep queues.
170 #if DIAGNOSTIC
171 if (chan == 0)
172 panic("tsleep");
173 #endif
176 * The sleep_semaphore protects the sleep queues and
177 * p_ fields in SocketBases.
179 * When the process is in a sleep queue its p_wchan field is nonzero.
181 ObtainSemaphore(&sleep_semaphore);
182 p->p_wchan = chan;
183 p->p_wmesg = wmesg;
184 q = &sleep_queue[SLEEP_HASH(chan)];
185 queue_enter(q, p, struct SocketBase *, p_sleep_link);
186 ReleaseSemaphore(&sleep_semaphore);
190 tsleep_main(struct SocketBase *p, ULONG wakemask)
192 ULONG sigmask, bmask, timermask;
193 struct timerequest *timerReply;
194 register queue_t q;
195 int result;
196 #if defined(__AROS__)
197 D(bug("[AROSTCP](kern_synch.c) tsleep_main()\n"));
198 #endif
200 * Set the signal mask for the wait
202 timermask = 1 << p->timerPort->mp_SigBit;
203 sigmask = timermask | p->sigIntrMask | wakemask;
205 for (;;) {
207 * wait for timeout, wakeup or interrupt
209 bmask = Wait(sigmask);
212 * Check if we were interrupted
214 if (bmask & p->sigIntrMask & ~wakemask) {
215 result = EINTR;
216 break;
220 * Check for user signals
222 if (bmask & wakemask) {
223 result = ERESTART;
224 break;
228 * check if we were woken up.
230 * If p->p_chan is zero then the wakener has removed us from
231 * the sleep queue.
233 if (p->p_wchan == 0) {
235 * Set back the signals which interrupted us so that user program can
236 * detect them
238 bmask &= p->sigIntrMask|wakemask;
239 if (bmask)
240 SetSignal(bmask, bmask);
242 return 0; /* return success */
246 * check if we got the timer reply signal and message
248 if (bmask & timermask &&
249 (timerReply = (struct timerequest *)GetMsg(p->timerPort)) &&
250 timerReply == p->tsleep_timer) { /* sanity check */
252 * timeout expired.
254 * Set the node type to NT_UNKNOWN to mark that it is referenced only by
255 * the p->tsleep_timer.
257 timerReply->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;
259 result = EWOULDBLOCK;
260 break;
263 } /* for */
265 /* Return path when sleeper has to be removed from the sleep queue */
268 * Set back the signals which interrupted us so that user program can
269 * detect them
271 bmask &= p->sigIntrMask | wakemask;
272 if (bmask)
273 SetSignal(bmask, bmask);
276 * remove from the sleep queue
278 ObtainSemaphore(&sleep_semaphore);
280 * If p_chan is nonzero then we still are on the sleep queue and
281 * need to be removed from there.
283 if (p->p_wchan != 0) {
284 q = &sleep_queue[SLEEP_HASH(p->p_wchan)];
285 p->p_wchan = (char *)0;
286 queue_remove(q, p, struct SocketBase *, p_sleep_link);
288 ReleaseSemaphore(&sleep_semaphore);
290 return result;
294 * General sleep call.
295 * NOTE: caller is assumed to hold the syscall_semaphore! \* XXX *\
296 * Suspends current process until a wakeup is made on chan.
297 * Sleeps at most the time specified in a time_out (NULL means no timeout).
298 * Lowers the current spl-level to 0 while in sleep.
299 * Returns 0 if awakened, EWOULDBLOCK if the timeout expires and
300 * EINTR if interrupted.
303 tsleep(struct SocketBase *p, /* Library base through which this call came */
304 caddr_t chan, /* 'channel' to wait on */
305 const char *wmesg, /* reason to sleep */
306 const struct timeval *time_out) /* timeout as timeval structure */
308 int result;
309 spl_t old_spl;
310 #if defined(__AROS__)
311 D(bug("[AROSTCP](kern_synch.c) tsleep()\n"));
312 #endif
314 #if DIAGNOSTIC
315 extern struct Task *AROSTCP_Task;
316 if (FindTask(NULL) == AROSTCP_Task) {
317 log(LOG_ERR, "TCP/IP stack did tsleep() itself!");
318 return (-1);
320 #endif
322 #if DIAGNOSTIC
323 if (p == NULL) {
324 log(LOG_ERR, "tsleep() called with NULL SocketBase pointer!");
325 return (-1);
327 #endif
329 #if DIAGNOSTIC
330 if (FindTask(NULL) != syscall_semaphore.ss_Owner) {
331 log(LOG_ERR, "tsleep() called with NO syscall_semaphore!");
332 return (-1);
334 #endif
336 tsleep_send_timeout(p, time_out);
338 tsleep_enter(p, chan, wmesg);
341 * release spl-level while in sleep.
343 * NOTE: syscall_semaphore must be freed as well!
346 old_spl = spl0();
347 ReleaseSemaphore(&syscall_semaphore); /* XXX */
349 result = tsleep_main(p, 0);
352 * return old spl-level
354 ObtainSemaphore(&syscall_semaphore); /* XXX */
355 splx(old_spl);
358 * abort the timeout request if necessary
360 if (result != EWOULDBLOCK)
361 tsleep_abort_timeout(p, time_out);
363 return (result);
366 void
367 wakeup(caddr_t chan)
369 register queue_t q;
370 struct SocketBase *p, *next;
371 #if defined(__AROS__)
372 D(bug("[AROSTCP](kern_synch.c) wakeup()\n"));
373 #endif
375 #if DIAGNOSTIC
376 if (chan == 0) {
377 log(LOG_ERR, "wakeup on chan zero");
378 return;
380 #endif
382 ObtainSemaphore(&sleep_semaphore);
383 q = &sleep_queue[SLEEP_HASH(chan)];
385 p = (struct SocketBase *)queue_first(q);
386 while (!queue_end(q, (queue_entry_t)p)) {
387 next = (struct SocketBase *)queue_next(&p->p_sleep_link);
388 if (p->p_wchan == chan) {
390 * mark sleeper as woken up
392 p->p_wchan = NULL;
394 * remove sleeper from the sleep queue
396 queue_remove(q, p, struct SocketBase *, p_sleep_link);
398 * signal process to take attention
400 Signal(p->thisTask, 1 << p->timerPort->mp_SigBit);
402 p = next;
404 ReleaseSemaphore(&sleep_semaphore);
408 * Spl-level emulation:
410 * In this implementation the processor priority levels are modelled
411 * either with one semaphore (ifdef DEBUG) or by Exec Task switch
412 * disabling feature. Semaphore is used while debugging for security (to
413 * be able to single step almost everywhere). The production version uses
414 * ExecBase's TDNestCnt for speed (prevent unnecessary task switches).
416 * Note that both ways lead to the fact that when someone sets, say, splimp()
417 * he will WAIT for the holder of splnet() to finish.
419 * N.B. The above comments are now out-of-date. The semaphore implementation
420 * is now always used.
423 /*#ifndef DEBUG*/ /* NC */
424 #if 1
426 * spl_semaphore is used as mutex for all spl-levels
428 * Note that InitSemaphore() requires the signalSemaphore to be initialized
429 * to zero (here done statically).
431 struct SignalSemaphore spl_semaphore = { };
434 * spl_level holds the current pseudo priority level.
435 * NOTE: this may be accessed only while holding the spl_semaphore.
437 spl_t spl_level = SPL0;
438 static BOOL spl_initialized = FALSE;
440 BOOL
441 spl_init(void)
443 #if defined(__AROS__)
444 D(bug("[AROSTCP](kern_synch.c) spl_init()\n"));
445 #endif
446 if (!spl_initialized) {
448 * Initialize spl_semaphore for use. After this call any number of
449 * tasks may use spl-functions.
451 InitSemaphore(&spl_semaphore);
452 spl_initialized = TRUE;
454 return TRUE;
457 spl_t
458 spl_n(spl_t new_level)
460 register spl_t old_level;
461 /* Uncomment the following lines to get debug - however please note: this func runs often */
462 //#if defined(__AROS__)
463 //D(bug("[AROSTCP](kern_synch.c) spl_n()\n"));
464 //#endif
466 ObtainSemaphore(&spl_semaphore);
467 old_level = spl_level;
468 spl_level = new_level;
470 if (new_level > old_level) { /* raise level */
471 if (old_level == 0) /* lock when raising over zero */
473 * so. return without releasing the lock
475 return old_level;
477 else if (new_level < old_level) { /* lower level */
478 if (new_level == 0) /* unlock when lowering to zero */
480 * now release the lock kept above
482 ReleaseSemaphore(&spl_semaphore);
484 ReleaseSemaphore(&spl_semaphore);
486 return old_level;
489 #else
491 BOOL
492 spl_init(void)
494 #if defined(__AROS__)
495 D(bug("[AROSTCP](kern_synch.c) spl_init() VOID\n"));
496 #endif
497 return TRUE;
501 * spl_n is defined as an inline in <sys/synch.h>
503 #endif /* DEBUG */