2 * Copyright (C) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
3 * Helsinki University of Technology, Finland.
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,
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.
67 D(bug("[AROSTCP](kern_synch.c) sleep_init()\n"));
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
;
88 tsleep_send_timeout(struct SocketBase
*p
,
89 const struct timeval
*time_out
)
92 D(bug("[AROSTCP](kern_synch.c) tsleep_send_timeout()\n"));
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
;
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
);
124 * send timeout request if necessary
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
));
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"));
150 /* do not signal us any more */
151 p
->timerPort
->mp_Flags
= PA_IGNORE
;
156 tsleep_enter(struct SocketBase
*p
,
157 caddr_t chan
, /* 'channel' to wait on */
158 const char *wmesg
) /* reason to sleep */
161 #if defined(__AROS__)
162 D(bug("[AROSTCP](kern_synch.c) tsleep_enter()\n"));
165 * Zero is a reserved value, used to indicate
166 * that we have been woken up and are no longer on
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
);
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
;
196 #if defined(__AROS__)
197 D(bug("[AROSTCP](kern_synch.c) tsleep_main()\n"));
200 * Set the signal mask for the wait
202 timermask
= 1 << p
->timerPort
->mp_SigBit
;
203 sigmask
= timermask
| p
->sigIntrMask
| wakemask
;
207 * wait for timeout, wakeup or interrupt
209 bmask
= Wait(sigmask
);
212 * Check if we were interrupted
214 if (bmask
& p
->sigIntrMask
& ~wakemask
) {
220 * Check for user signals
222 if (bmask
& wakemask
) {
228 * check if we were woken up.
230 * If p->p_chan is zero then the wakener has removed us from
233 if (p
->p_wchan
== 0) {
235 * Set back the signals which interrupted us so that user program can
238 bmask
&= p
->sigIntrMask
|wakemask
;
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 */
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
;
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
271 bmask
&= p
->sigIntrMask
| wakemask
;
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
);
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 */
310 #if defined(__AROS__)
311 D(bug("[AROSTCP](kern_synch.c) tsleep()\n"));
315 extern struct Task
*AROSTCP_Task
;
316 if (FindTask(NULL
) == AROSTCP_Task
) {
317 log(LOG_ERR
, "TCP/IP stack did tsleep() itself!");
324 log(LOG_ERR
, "tsleep() called with NULL SocketBase pointer!");
330 if (FindTask(NULL
) != syscall_semaphore
.ss_Owner
) {
331 log(LOG_ERR
, "tsleep() called with NO syscall_semaphore!");
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!
347 ReleaseSemaphore(&syscall_semaphore
); /* XXX */
349 result
= tsleep_main(p
, 0);
352 * return old spl-level
354 ObtainSemaphore(&syscall_semaphore
); /* XXX */
358 * abort the timeout request if necessary
360 if (result
!= EWOULDBLOCK
)
361 tsleep_abort_timeout(p
, time_out
);
370 struct SocketBase
*p
, *next
;
371 #if defined(__AROS__)
372 D(bug("[AROSTCP](kern_synch.c) wakeup()\n"));
377 log(LOG_ERR
, "wakeup on chan zero");
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
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
);
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 */
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
;
443 #if defined(__AROS__)
444 D(bug("[AROSTCP](kern_synch.c) spl_init()\n"));
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
;
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"));
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
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
);
494 #if defined(__AROS__)
495 D(bug("[AROSTCP](kern_synch.c) spl_init() VOID\n"));
501 * spl_n is defined as an inline in <sys/synch.h>