Remove obsolete INITLIST macro.
[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 devlib/README 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.7  2004/11/28 23:20:25  bernie
19  *#* Remove obsolete INITLIST macro.
20  *#*
21  *#* Revision 1.6  2004/10/21 10:57:21  bernie
22  *#* Use proc_forbid()/proc_permit().
23  *#*
24  *#* Revision 1.5  2004/10/21 10:48:57  bernie
25  *#* sem_release(): Simplify (made by rasky on scfirm).
26  *#*
27  *#* Revision 1.4  2004/08/25 14:12:09  rasky
28  *#* Aggiornato il comment block dei log RCS
29  *#*
30  *#* Revision 1.3  2004/08/08 05:53:23  bernie
31  *#* Use DISABLE_IRQSAVE/ENABLE_IRQRESTORE; Cleanup documentation.
32  *#*
33  *#* Revision 1.2  2004/06/03 11:27:09  bernie
34  *#* Add dual-license information.
35  *#*
36  *#* Revision 1.1  2004/05/23 17:27:00  bernie
37  *#* Import kern/ subdirectory.
38  *#*/
39
40 #include "sem.h"
41 #include "proc.h"
42 #include "proc_p.h"
43 #include "signal.h"
44 #include "hw.h"
45
46
47 /*!
48  * \brief Initialize a Semaphore structure.
49  */
50 void sem_init(struct Semaphore *s)
51 {
52         LIST_INIT(&s->wait_queue);
53         s->owner = NULL;
54         s->nest_count = 0;
55 }
56
57
58 /*!
59  * \brief Attempt to lock a semaphore without waiting.
60  *
61  * \return true in case of success, false if the semaphore
62  *         was already locked by someone else.
63  *
64  * \note   each call to sem_attempt() must be matched by a
65  *         call to sem_release().
66  *
67  * \see sem_obtain() sem_release()
68  */
69 bool sem_attempt(struct Semaphore *s)
70 {
71         bool result = false;
72
73         proc_forbid();
74         if ((!s->owner) || (s->owner == CurrentProcess))
75         {
76                 s->owner = CurrentProcess;
77                 s->nest_count++;
78                 result = true;
79         }
80         proc_permit();
81
82         return result;
83 }
84
85
86 /*!
87  * \brief Lock a semaphore.
88  *
89  * If the semaphore is already owned by another process, the caller
90  * process will be enqueued into the waiting list and sleep until
91  * the semaphore is available.
92  *
93  * \note Each call to sem_obtain() must be matched by a
94  *       call to sem_release().
95  *
96  * \note This routine is optimized for highest speed in
97  *       the most common case: the semaphore is free or locked
98  *       by the calling process itself. Rearranging this code
99  *       is probably a bad idea.
100  *
101  * \sa sem_release() sem_attempt()
102  */
103 void sem_obtain(struct Semaphore *s)
104 {
105         proc_forbid();
106
107         /* Is the semaphore already locked by another process? */
108         if (UNLIKELY(s->owner && (s->owner != CurrentProcess)))
109         {
110                 /* Append calling process to the wait queue */
111                 ADDTAIL(&s->wait_queue, (Node *)CurrentProcess);
112
113                 /*
114                  * We will wake up only when the current owner calls
115                  * sem_release(). Then, the semaphore will already
116                  * be locked for us.
117                  */
118                 proc_permit();
119                 proc_schedule();
120         }
121         else
122         {
123                 /* The semaphore was free: lock it */
124                 s->owner = CurrentProcess;
125                 s->nest_count++;
126                 proc_permit();
127         }
128 }
129
130
131 /*!
132  * \brief Release a lock on a previously locked semaphore.
133  *
134  * If the nesting count of the semaphore reaches zero,
135  * the next process waiting for it will be awaken.
136  *
137  * \note This routine is optimized for highest speed in
138  *       the most common case: the semaphore has been locked just
139  *       once and nobody else was waiting for it. Rearranging
140  *       this code is probably a bad idea.
141  *
142  * \sa sem_obtain() sem_attempt()
143  */
144 void sem_release(struct Semaphore *s)
145 {
146         proc_forbid();
147
148         /*
149          * Decrement nesting count and check if the semaphore
150          * has been fully unlocked.
151          */
152         if (--s->nest_count == 0)
153         {
154                 Process *proc;
155
156                 /* Disown semaphore */
157                 s->owner = NULL;
158
159                 /* Give semaphore to the first applicant, if any */
160                 if (UNLIKELY((proc = (Process *)REMHEAD(&s->wait_queue))))
161                 {
162                         s->nest_count = 1;
163                         s->owner = proc;
164                         SCHED_ENQUEUE(proc);
165                 }
166         }
167
168         proc_permit();
169 }