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