blob: d2e4dd2fa14d57a6d6c21d3f30755fcefe5e7c24 [file] [log] [blame]
adamdunkelsac205342004-08-17 19:43:30 +00001/**
2 * \addtogroup pt
3 * @{
4 */
5
6/**
7 * \defgroup ptsem Protothread semaphores
8 * @{
9 *
10 * This module implements counting semaphores on top of
11 * protothreads. Semaphores are a synchronization primitive that
12 * provide two operations: "wait" and "signal". The "wait" operation
13 * checks the semaphore counter and blocks the thread if the counter
14 * is zero. The "signal" operation increases the semaphore counter but
15 * does not block. If another thread has blocked waiting for the
16 * semaphore that is signalled, the blocked thread will become
17 * runnable again.
18 *
19 * Semaphores can be used to implement other, more structured,
20 * synchronization primitives such as monitors and message
21 * queues/bounded buffers (see below).
22 *
23 * The following example shows how the producer-consumer problem, also
24 * known as the bounded buffer problem, can be solved using
25 * protothreads and semaphores. Notes on the program follow after the
26 * example.
27 *
28 \code
29#include "pt-sem.h"
30
31#define NUM_ITEMS 32
32#define BUFSIZE 8
33
34static struct pt_sem mutex, full, empty;
35
36PT_THREAD(producer(struct pt *pt))
37{
38 static int produced;
39
40 PT_START(pt);
41
42 for(produced = 0; produced < NUM_ITEMS; ++produced) {
43
44 PT_SEM_WAIT(pt, &full);
45
46 PT_SEM_WAIT(pt, &mutex);
47 add_to_buffer(produce_item());
48 PT_SEM_SIGNAL(pt, &mutex);
49
50 PT_SEM_SIGNAL(pt, &empty);
51 }
52
53 PT_EXIT(pt);
54}
55
56PT_THREAD(consumer(struct pt *pt))
57{
58 static int consumed;
59
60 PT_START(pt);
61
62 for(consumed = 0; consumed < NUM_ITEMS; ++consumed) {
63
64 PT_SEM_WAIT(pt, &empty);
65
66 PT_SEM_WAIT(pt, &mutex);
67 consume_item(get_from_buffer());
68 PT_SEM_SIGNAL(pt, &mutex);
69
70 PT_SEM_SIGNAL(pt, &full);
71 }
72
73 PT_EXIT(pt);
74}
75
76PT_THREAD(driver_thread(struct pt *pt))
77{
78 static struct pt pt_producer, pt_consumer;
79
80 PT_START(pt);
81
82 PT_SEM_INIT(&empty, 0);
83 PT_SEM_INIT(&full, BUFSIZE);
84 PT_SEM_INIT(&mutex, 1);
85
86 PT_INIT(&pt_producer);
87 PT_INIT(&pt_consumer);
88
89 PT_WAIT_THREAD(pt, producer(&pt_producer) &
90 consumer(&pt_consumer));
91
92 PT_EXIT(pt);
93}
94 \endcode
95 *
96 * The program uses three protothreads: one protothread that
97 * implements the consumer, one thread that implements the producer,
98 * and one protothread that drives the two other protothreads. The
99 * program uses three semaphores: "full", "empty" and "mutex". The
100 * "mutex" semaphore is used to provide mutual exclusion for the
101 * buffer, the "empty" semaphore is used to block the consumer is the
102 * buffer is empty, and the "full" semaphore is used to block the
103 * producer is the buffer is full.
104 *
105 * The "driver_thread" holds two protothread state variables,
106 * "pt_producer" and "pt_consumer". It is important to note that both
107 * these variables are declared as <i>static</i>. If the static
108 * keyword is not used, both variables are stored on the stack. Since
109 * protothreads do not store the stack, these variables may be
110 * overwritten during a protothread wait operation. Similarly, both
111 * the "consumer" and "producer" protothreads declare their local
112 * variables as static, to avoid them being stored on the stack.
113 *
114 *
115 */
116
117/**
118 * \file
119 * Couting semaphores implemented on protothreads
120 * \author
121 * Adam Dunkels <adam@sics.se>
122 *
123 */
124
125#ifndef __PT_SEM_H__
126#define __PT_SEM_H__
127
128#include "pt.h"
129
130struct pt_sem {
131 unsigned int count;
132};
133
134/**
135 * Initialize a semaphore
136 *
137 * This macro initializes a semaphore with a value for the
138 * counter. Internally, the semaphores use an "unsigned int" to
139 * represent the counter, and therefore the "count" argument should be
140 * within range of an unsigned int.
141 *
142 * \param s (struct pt_sem *) A pointer to the pt_sem struct
143 * representing the semaphore
144 *
145 * \param c (unsigned int) The initial count of the semaphore.
146 * \hideinitializer
147 */
148#define PT_SEM_INIT(s, c) (s)->count = c
149
150/**
151 * Wait for a semaphore
152 *
153 * This macro carries out the "wait" operation on the semaphore. The
154 * wait operation causes the protothread to block while the counter is
155 * zero. When the counter reaches a value larger than zero, the
156 * protothread will continue.
157 *
158 * \param pt (struct pt *) A pointer to the protothread (struct pt) in
159 * which the operation is executed.
160 *
161 * \param s (struct pt_sem *) A pointer to the pt_sem struct
162 * representing the semaphore
163 *
164 * \hideinitializer
165 */
166#define PT_SEM_WAIT(pt, s) \
167 do { \
168 PT_WAIT_UNTIL(pt, (s)->count > 0); \
169 --(s)->count; \
170 } while(0)
171
172/**
173 * Signal a semaphore
174 *
175 * This macro carries out the "signal" operation on the semaphore. The
176 * signal operation increments the counter inside the semaphore, which
177 * eventually will cause waiting protothreads to continue executing.
178 *
179 * \param pt (struct pt *) A pointer to the protothread (struct pt) in
180 * which the operation is executed.
181 *
182 * \param s (struct pt_sem *) A pointer to the pt_sem struct
183 * representing the semaphore
184 *
185 * \hideinitializer
186 */
187#define PT_SEM_SIGNAL(pt, s) ++(s)->count
188
189#endif /* __PT_SEM_H__ */
190
191/** @} */
192/** @} */
193