blob: 6d1907e065108c45f1606345db7ac65579110f70 [file] [log] [blame]
adamdunkelsa2f3c422004-09-12 20:24:53 +00001/*
2 * Copyright (c) 2004, Swedish Institute of Computer Science.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 *
31 * Author: Adam Dunkels <adam@sics.se>
32 *
adamdunkelscfc43402005-02-28 09:02:41 +000033 * $Id: pt-sem.h,v 1.4 2005/02/28 09:02:41 adamdunkels Exp $
adamdunkelsa2f3c422004-09-12 20:24:53 +000034 */
35
adamdunkelsac205342004-08-17 19:43:30 +000036/**
37 * \addtogroup pt
38 * @{
39 */
40
41/**
42 * \defgroup ptsem Protothread semaphores
43 * @{
44 *
45 * This module implements counting semaphores on top of
46 * protothreads. Semaphores are a synchronization primitive that
47 * provide two operations: "wait" and "signal". The "wait" operation
48 * checks the semaphore counter and blocks the thread if the counter
49 * is zero. The "signal" operation increases the semaphore counter but
50 * does not block. If another thread has blocked waiting for the
51 * semaphore that is signalled, the blocked thread will become
52 * runnable again.
53 *
54 * Semaphores can be used to implement other, more structured,
55 * synchronization primitives such as monitors and message
56 * queues/bounded buffers (see below).
57 *
58 * The following example shows how the producer-consumer problem, also
59 * known as the bounded buffer problem, can be solved using
60 * protothreads and semaphores. Notes on the program follow after the
61 * example.
62 *
63 \code
64#include "pt-sem.h"
65
66#define NUM_ITEMS 32
67#define BUFSIZE 8
68
69static struct pt_sem mutex, full, empty;
70
71PT_THREAD(producer(struct pt *pt))
72{
73 static int produced;
74
adamdunkelsaa930f92004-09-13 21:47:46 +000075 PT_BEGIN(pt);
adamdunkelsac205342004-08-17 19:43:30 +000076
77 for(produced = 0; produced < NUM_ITEMS; ++produced) {
78
79 PT_SEM_WAIT(pt, &full);
80
81 PT_SEM_WAIT(pt, &mutex);
82 add_to_buffer(produce_item());
83 PT_SEM_SIGNAL(pt, &mutex);
84
85 PT_SEM_SIGNAL(pt, &empty);
86 }
87
adamdunkelsaa930f92004-09-13 21:47:46 +000088 PT_END(pt);
adamdunkelsac205342004-08-17 19:43:30 +000089}
90
91PT_THREAD(consumer(struct pt *pt))
92{
93 static int consumed;
94
adamdunkelsaa930f92004-09-13 21:47:46 +000095 PT_BEGIN(pt);
adamdunkelsac205342004-08-17 19:43:30 +000096
97 for(consumed = 0; consumed < NUM_ITEMS; ++consumed) {
98
99 PT_SEM_WAIT(pt, &empty);
100
101 PT_SEM_WAIT(pt, &mutex);
102 consume_item(get_from_buffer());
103 PT_SEM_SIGNAL(pt, &mutex);
104
105 PT_SEM_SIGNAL(pt, &full);
106 }
107
adamdunkelsaa930f92004-09-13 21:47:46 +0000108 PT_END(pt);
adamdunkelsac205342004-08-17 19:43:30 +0000109}
110
111PT_THREAD(driver_thread(struct pt *pt))
112{
113 static struct pt pt_producer, pt_consumer;
114
adamdunkelsaa930f92004-09-13 21:47:46 +0000115 PT_BEGIN(pt);
adamdunkelsac205342004-08-17 19:43:30 +0000116
117 PT_SEM_INIT(&empty, 0);
118 PT_SEM_INIT(&full, BUFSIZE);
119 PT_SEM_INIT(&mutex, 1);
120
121 PT_INIT(&pt_producer);
122 PT_INIT(&pt_consumer);
123
124 PT_WAIT_THREAD(pt, producer(&pt_producer) &
125 consumer(&pt_consumer));
126
adamdunkelsaa930f92004-09-13 21:47:46 +0000127 PT_END(pt);
adamdunkelsac205342004-08-17 19:43:30 +0000128}
129 \endcode
130 *
131 * The program uses three protothreads: one protothread that
132 * implements the consumer, one thread that implements the producer,
133 * and one protothread that drives the two other protothreads. The
134 * program uses three semaphores: "full", "empty" and "mutex". The
135 * "mutex" semaphore is used to provide mutual exclusion for the
136 * buffer, the "empty" semaphore is used to block the consumer is the
137 * buffer is empty, and the "full" semaphore is used to block the
138 * producer is the buffer is full.
139 *
140 * The "driver_thread" holds two protothread state variables,
141 * "pt_producer" and "pt_consumer". It is important to note that both
142 * these variables are declared as <i>static</i>. If the static
143 * keyword is not used, both variables are stored on the stack. Since
144 * protothreads do not store the stack, these variables may be
145 * overwritten during a protothread wait operation. Similarly, both
146 * the "consumer" and "producer" protothreads declare their local
147 * variables as static, to avoid them being stored on the stack.
148 *
149 *
150 */
151
152/**
153 * \file
adamdunkelscfc43402005-02-28 09:02:41 +0000154 * Counting semaphores implemented on protothreads
adamdunkelsac205342004-08-17 19:43:30 +0000155 * \author
156 * Adam Dunkels <adam@sics.se>
157 *
158 */
159
160#ifndef __PT_SEM_H__
161#define __PT_SEM_H__
162
163#include "pt.h"
164
165struct pt_sem {
166 unsigned int count;
167};
168
169/**
170 * Initialize a semaphore
171 *
172 * This macro initializes a semaphore with a value for the
173 * counter. Internally, the semaphores use an "unsigned int" to
174 * represent the counter, and therefore the "count" argument should be
175 * within range of an unsigned int.
176 *
177 * \param s (struct pt_sem *) A pointer to the pt_sem struct
178 * representing the semaphore
179 *
180 * \param c (unsigned int) The initial count of the semaphore.
181 * \hideinitializer
182 */
183#define PT_SEM_INIT(s, c) (s)->count = c
184
185/**
186 * Wait for a semaphore
187 *
188 * This macro carries out the "wait" operation on the semaphore. The
189 * wait operation causes the protothread to block while the counter is
190 * zero. When the counter reaches a value larger than zero, the
191 * protothread will continue.
192 *
193 * \param pt (struct pt *) A pointer to the protothread (struct pt) in
194 * which the operation is executed.
195 *
196 * \param s (struct pt_sem *) A pointer to the pt_sem struct
197 * representing the semaphore
198 *
199 * \hideinitializer
200 */
201#define PT_SEM_WAIT(pt, s) \
202 do { \
203 PT_WAIT_UNTIL(pt, (s)->count > 0); \
204 --(s)->count; \
205 } while(0)
206
207/**
208 * Signal a semaphore
209 *
210 * This macro carries out the "signal" operation on the semaphore. The
211 * signal operation increments the counter inside the semaphore, which
212 * eventually will cause waiting protothreads to continue executing.
213 *
214 * \param pt (struct pt *) A pointer to the protothread (struct pt) in
215 * which the operation is executed.
216 *
217 * \param s (struct pt_sem *) A pointer to the pt_sem struct
218 * representing the semaphore
219 *
220 * \hideinitializer
221 */
222#define PT_SEM_SIGNAL(pt, s) ++(s)->count
223
224#endif /* __PT_SEM_H__ */
225
226/** @} */
227/** @} */
228