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