Fix reference to README.devlib in header.
[bertos.git] / kern / sem.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2001, 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999, 2000, 2001 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \brief Semaphore based synchronization services.
10  *
11  * \version $Id$
12  *
13  * \author Bernardo Innocenti <bernie@develer.com>
14  */
15
16 /*#*
17  *#* $Log$
18  *#* Revision 1.10  2005/11/04 16:20:02  bernie
19  *#* Fix reference to README.devlib in header.
20  *#*
21  *#* Revision 1.9  2005/04/11 19:10:28  bernie
22  *#* Include top-level headers from cfg/ subdir.
23  *#*
24  *#* Revision 1.8  2005/01/22 04:20:42  bernie
25  *#* Add integrity checks.
26  *#*
27  *#* Revision 1.7  2004/11/28 23:20:25  bernie
28  *#* Remove obsolete INITLIST macro.
29  *#*
30  *#* Revision 1.6  2004/10/21 10:57:21  bernie
31  *#* Use proc_forbid()/proc_permit().
32  *#*
33  *#* Revision 1.5  2004/10/21 10:48:57  bernie
34  *#* sem_release(): Simplify (made by rasky on scfirm).
35  *#*
36  *#* Revision 1.4  2004/08/25 14:12:09  rasky
37  *#* Aggiornato il comment block dei log RCS
38  *#*
39  *#* Revision 1.3  2004/08/08 05:53:23  bernie
40  *#* Use DISABLE_IRQSAVE/ENABLE_IRQRESTORE; Cleanup documentation.
41  *#*
42  *#* Revision 1.2  2004/06/03 11:27:09  bernie
43  *#* Add dual-license information.
44  *#*
45  *#* Revision 1.1  2004/05/23 17:27:00  bernie
46  *#* Import kern/ subdirectory.
47  *#*/
48
49 #include "sem.h"
50 #include "proc.h"
51 #include "proc_p.h"
52 #include "signal.h"
53 #include "hw.h"
54 #include <cfg/debug.h>
55
56 INLINE void sem_verify(struct Semaphore *s)
57 {
58         LIST_ASSERT_VALID(&s->wait_queue);
59         ASSERT(s->nest_count >= 0);
60         ASSERT(s->nest_count < 128);   // heuristic max
61 }
62
63
64 /*!
65  * \brief Initialize a Semaphore structure.
66  */
67 void sem_init(struct Semaphore *s)
68 {
69         LIST_INIT(&s->wait_queue);
70         s->owner = NULL;
71         s->nest_count = 0;
72 }
73
74
75 /*!
76  * \brief Attempt to lock a semaphore without waiting.
77  *
78  * \return true in case of success, false if the semaphore
79  *         was already locked by someone else.
80  *
81  * \note   each call to sem_attempt() must be matched by a
82  *         call to sem_release().
83  *
84  * \see sem_obtain() sem_release()
85  */
86 bool sem_attempt(struct Semaphore *s)
87 {
88         bool result = false;
89
90         proc_forbid();
91         sem_verify(s);
92         if ((!s->owner) || (s->owner == CurrentProcess))
93         {
94                 s->owner = CurrentProcess;
95                 s->nest_count++;
96                 result = true;
97         }
98         proc_permit();
99
100         return result;
101 }
102
103
104 /*!
105  * \brief Lock a semaphore.
106  *
107  * If the semaphore is already owned by another process, the caller
108  * process will be enqueued into the waiting list and sleep until
109  * the semaphore is available.
110  *
111  * \note Each call to sem_obtain() must be matched by a
112  *       call to sem_release().
113  *
114  * \note This routine is optimized for highest speed in
115  *       the most common case: the semaphore is free or locked
116  *       by the calling process itself. Rearranging this code
117  *       is probably a bad idea.
118  *
119  * \sa sem_release() sem_attempt()
120  */
121 void sem_obtain(struct Semaphore *s)
122 {
123         proc_forbid();
124         sem_verify(s);
125
126         /* Is the semaphore already locked by another process? */
127         if (UNLIKELY(s->owner && (s->owner != CurrentProcess)))
128         {
129                 /* Append calling process to the wait queue */
130                 ADDTAIL(&s->wait_queue, (Node *)CurrentProcess);
131
132                 /*
133                  * We will wake up only when the current owner calls
134                  * sem_release(). Then, the semaphore will already
135                  * be locked for us.
136                  */
137                 proc_permit();
138                 proc_schedule();
139         }
140         else
141         {
142                 ASSERT(ISLISTEMPTY(&s->wait_queue));
143
144                 /* The semaphore was free: lock it */
145                 s->owner = CurrentProcess;
146                 s->nest_count++;
147                 proc_permit();
148         }
149 }
150
151
152 /*!
153  * \brief Release a lock on a previously locked semaphore.
154  *
155  * If the nesting count of the semaphore reaches zero,
156  * the next process waiting for it will be awaken.
157  *
158  * \note This routine is optimized for highest speed in
159  *       the most common case: the semaphore has been locked just
160  *       once and nobody else was waiting for it. Rearranging
161  *       this code is probably a bad idea.
162  *
163  * \sa sem_obtain() sem_attempt()
164  */
165 void sem_release(struct Semaphore *s)
166 {
167         proc_forbid();
168         sem_verify(s);
169
170         ASSERT(s->owner == CurrentProcess);
171
172         /*
173          * Decrement nesting count and check if the semaphore
174          * has been fully unlocked.
175          */
176         if (--s->nest_count == 0)
177         {
178                 Process *proc;
179
180                 /* Disown semaphore */
181                 s->owner = NULL;
182
183                 /* Give semaphore to the first applicant, if any */
184                 if (UNLIKELY((proc = (Process *)REMHEAD(&s->wait_queue))))
185                 {
186                         s->nest_count = 1;
187                         s->owner = proc;
188                         SCHED_ENQUEUE(proc);
189                 }
190         }
191
192         proc_permit();
193 }