Remove unneeded debug message. Fix test function.
[bertos.git] / kern / sem.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2001, 2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999, 2000, 2001 Bernardo Innocenti <bernie@develer.com>
31  *
32  * -->
33  *
34  * \brief Semaphore based synchronization services.
35  *
36  * \version $Id$
37  *
38  * \author Bernardo Innocenti <bernie@develer.com>
39  */
40
41 /*#*
42  *#* $Log$
43  *#* Revision 1.12  2006/07/19 12:56:27  bernie
44  *#* Convert to new Doxygen style.
45  *#*
46  *#* Revision 1.11  2006/02/24 01:17:05  bernie
47  *#* Update for new emulator.
48  *#*
49  *#* Revision 1.10  2005/11/04 16:20:02  bernie
50  *#* Fix reference to README.devlib in header.
51  *#*
52  *#* Revision 1.9  2005/04/11 19:10:28  bernie
53  *#* Include top-level headers from cfg/ subdir.
54  *#*
55  *#* Revision 1.8  2005/01/22 04:20:42  bernie
56  *#* Add integrity checks.
57  *#*
58  *#* Revision 1.7  2004/11/28 23:20:25  bernie
59  *#* Remove obsolete INITLIST macro.
60  *#*
61  *#* Revision 1.6  2004/10/21 10:57:21  bernie
62  *#* Use proc_forbid()/proc_permit().
63  *#*
64  *#* Revision 1.5  2004/10/21 10:48:57  bernie
65  *#* sem_release(): Simplify (made by rasky on scfirm).
66  *#*
67  *#* Revision 1.4  2004/08/25 14:12:09  rasky
68  *#* Aggiornato il comment block dei log RCS
69  *#*
70  *#* Revision 1.3  2004/08/08 05:53:23  bernie
71  *#* Use DISABLE_IRQSAVE/ENABLE_IRQRESTORE; Cleanup documentation.
72  *#*
73  *#* Revision 1.2  2004/06/03 11:27:09  bernie
74  *#* Add dual-license information.
75  *#*
76  *#* Revision 1.1  2004/05/23 17:27:00  bernie
77  *#* Import kern/ subdirectory.
78  *#*/
79
80 #include "sem.h"
81 #include <kern/proc.h>
82 #include <kern/proc_p.h>
83 #include <kern/signal.h>
84 #include <cfg/debug.h>
85
86 INLINE void sem_verify(struct Semaphore *s)
87 {
88         LIST_ASSERT_VALID(&s->wait_queue);
89         ASSERT(s->nest_count >= 0);
90         ASSERT(s->nest_count < 128);   // heuristic max
91 }
92
93
94 /**
95  * \brief Initialize a Semaphore structure.
96  */
97 void sem_init(struct Semaphore *s)
98 {
99         LIST_INIT(&s->wait_queue);
100         s->owner = NULL;
101         s->nest_count = 0;
102 }
103
104
105 /**
106  * \brief Attempt to lock a semaphore without waiting.
107  *
108  * \return true in case of success, false if the semaphore
109  *         was already locked by someone else.
110  *
111  * \note   each call to sem_attempt() must be matched by a
112  *         call to sem_release().
113  *
114  * \see sem_obtain() sem_release()
115  */
116 bool sem_attempt(struct Semaphore *s)
117 {
118         bool result = false;
119
120         proc_forbid();
121         sem_verify(s);
122         if ((!s->owner) || (s->owner == CurrentProcess))
123         {
124                 s->owner = CurrentProcess;
125                 s->nest_count++;
126                 result = true;
127         }
128         proc_permit();
129
130         return result;
131 }
132
133
134 /**
135  * \brief Lock a semaphore.
136  *
137  * If the semaphore is already owned by another process, the caller
138  * process will be enqueued into the waiting list and sleep until
139  * the semaphore is available.
140  *
141  * \note Each call to sem_obtain() must be matched by a
142  *       call to sem_release().
143  *
144  * \note This routine is optimized for highest speed in
145  *       the most common case: the semaphore is free or locked
146  *       by the calling process itself. Rearranging this code
147  *       is probably a bad idea.
148  *
149  * \sa sem_release() sem_attempt()
150  */
151 void sem_obtain(struct Semaphore *s)
152 {
153         proc_forbid();
154         sem_verify(s);
155
156         /* Is the semaphore already locked by another process? */
157         if (UNLIKELY(s->owner && (s->owner != CurrentProcess)))
158         {
159                 /* Append calling process to the wait queue */
160                 ADDTAIL(&s->wait_queue, (Node *)CurrentProcess);
161
162                 /*
163                  * We will wake up only when the current owner calls
164                  * sem_release(). Then, the semaphore will already
165                  * be locked for us.
166                  */
167                 proc_permit();
168                 proc_schedule();
169         }
170         else
171         {
172                 ASSERT(LIST_EMPTY(&s->wait_queue));
173
174                 /* The semaphore was free: lock it */
175                 s->owner = CurrentProcess;
176                 s->nest_count++;
177                 proc_permit();
178         }
179 }
180
181
182 /**
183  * \brief Release a lock on a previously locked semaphore.
184  *
185  * If the nesting count of the semaphore reaches zero,
186  * the next process waiting for it will be awaken.
187  *
188  * \note This routine is optimized for highest speed in
189  *       the most common case: the semaphore has been locked just
190  *       once and nobody else was waiting for it. Rearranging
191  *       this code is probably a bad idea.
192  *
193  * \sa sem_obtain() sem_attempt()
194  */
195 void sem_release(struct Semaphore *s)
196 {
197         proc_forbid();
198         sem_verify(s);
199
200         ASSERT(s->owner == CurrentProcess);
201
202         /*
203          * Decrement nesting count and check if the semaphore
204          * has been fully unlocked.
205          */
206         if (--s->nest_count == 0)
207         {
208                 Process *proc;
209
210                 /* Disown semaphore */
211                 s->owner = NULL;
212
213                 /* Give semaphore to the first applicant, if any */
214                 if (UNLIKELY((proc = (Process *)list_remHead(&s->wait_queue))))
215                 {
216                         s->nest_count = 1;
217                         s->owner = proc;
218                         SCHED_ENQUEUE(proc);
219                 }
220         }
221
222         proc_permit();
223 }