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