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