Contiki 2.5
process.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2005, 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  * @(#)$Id: process.c,v 1.12 2010/10/20 22:24:46 adamdunkels Exp $
32  */
33 
34 /**
35  * \addtogroup process
36  * @{
37  */
38 
39 /**
40  * \file
41  * Implementation of the Contiki process kernel.
42  * \author
43  * Adam Dunkels <adam@sics.se>
44  *
45  */
46 
47 #include <stdio.h>
48 
49 #include "sys/process.h"
50 #include "sys/arg.h"
51 
52 /*
53  * Pointer to the currently running process structure.
54  */
55 struct process *process_list = NULL;
56 struct process *process_current = NULL;
57 
58 static process_event_t lastevent;
59 
60 /*
61  * Structure used for keeping the queue of active events.
62  */
63 struct event_data {
64  process_event_t ev;
65  process_data_t data;
66  struct process *p;
67 };
68 
69 static process_num_events_t nevents, fevent;
70 static struct event_data events[PROCESS_CONF_NUMEVENTS];
71 
72 #if PROCESS_CONF_STATS
73 process_num_events_t process_maxevents;
74 #endif
75 
76 static volatile unsigned char poll_requested;
77 
78 #define PROCESS_STATE_NONE 0
79 #define PROCESS_STATE_RUNNING 1
80 #define PROCESS_STATE_CALLED 2
81 
82 static void call_process(struct process *p, process_event_t ev, process_data_t data);
83 
84 #define DEBUG 0
85 #if DEBUG
86 #include <stdio.h>
87 #define PRINTF(...) printf(__VA_ARGS__)
88 #else
89 #define PRINTF(...)
90 #endif
91 
92 /*---------------------------------------------------------------------------*/
93 process_event_t
95 {
96  return lastevent++;
97 }
98 /*---------------------------------------------------------------------------*/
99 void
100 process_start(struct process *p, const char *arg)
101 {
102  struct process *q;
103 
104  /* First make sure that we don't try to start a process that is
105  already running. */
106  for(q = process_list; q != p && q != NULL; q = q->next);
107 
108  /* If we found the process on the process list, we bail out. */
109  if(q == p) {
110  return;
111  }
112  /* Put on the procs list.*/
113  p->next = process_list;
114  process_list = p;
115  p->state = PROCESS_STATE_RUNNING;
116  PT_INIT(&p->pt);
117 
118  PRINTF("process: starting '%s'\n", PROCESS_NAME_STRING(p));
119 
120  /* Post a synchronous initialization event to the process. */
121  process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg);
122 }
123 /*---------------------------------------------------------------------------*/
124 static void
125 exit_process(struct process *p, struct process *fromprocess)
126 {
127  register struct process *q;
128  struct process *old_current = process_current;
129 
130  PRINTF("process: exit_process '%s'\n", PROCESS_NAME_STRING(p));
131 
132  /* Make sure the process is in the process list before we try to
133  exit it. */
134  for(q = process_list; q != p && q != NULL; q = q->next);
135  if(q == NULL) {
136  return;
137  }
138 
139  if(process_is_running(p)) {
140  /* Process was running */
141  p->state = PROCESS_STATE_NONE;
142 
143  /*
144  * Post a synchronous event to all processes to inform them that
145  * this process is about to exit. This will allow services to
146  * deallocate state associated with this process.
147  */
148  for(q = process_list; q != NULL; q = q->next) {
149  if(p != q) {
150  call_process(q, PROCESS_EVENT_EXITED, (process_data_t)p);
151  }
152  }
153 
154  if(p->thread != NULL && p != fromprocess) {
155  /* Post the exit event to the process that is about to exit. */
156  process_current = p;
157  p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL);
158  }
159  }
160 
161  if(p == process_list) {
162  process_list = process_list->next;
163  } else {
164  for(q = process_list; q != NULL; q = q->next) {
165  if(q->next == p) {
166  q->next = p->next;
167  break;
168  }
169  }
170  }
171 
172  process_current = old_current;
173 }
174 /*---------------------------------------------------------------------------*/
175 static void
176 call_process(struct process *p, process_event_t ev, process_data_t data)
177 {
178  int ret;
179 
180 #if DEBUG
181  if(p->state == PROCESS_STATE_CALLED) {
182  printf("process: process '%s' called again with event %d\n", PROCESS_NAME_STRING(p), ev);
183  }
184 #endif /* DEBUG */
185 
186  if((p->state & PROCESS_STATE_RUNNING) &&
187  p->thread != NULL) {
188  PRINTF("process: calling process '%s' with event %d\n", PROCESS_NAME_STRING(p), ev);
189  process_current = p;
190  p->state = PROCESS_STATE_CALLED;
191  ret = p->thread(&p->pt, ev, data);
192  if(ret == PT_EXITED ||
193  ret == PT_ENDED ||
194  ev == PROCESS_EVENT_EXIT) {
195  exit_process(p, p);
196  } else {
197  p->state = PROCESS_STATE_RUNNING;
198  }
199  }
200 }
201 /*---------------------------------------------------------------------------*/
202 void
203 process_exit(struct process *p)
204 {
205  exit_process(p, PROCESS_CURRENT());
206 }
207 /*---------------------------------------------------------------------------*/
208 void
210 {
211  lastevent = PROCESS_EVENT_MAX;
212 
213  nevents = fevent = 0;
214 #if PROCESS_CONF_STATS
215  process_maxevents = 0;
216 #endif /* PROCESS_CONF_STATS */
217 
218  process_current = process_list = NULL;
219 }
220 /*---------------------------------------------------------------------------*/
221 /*
222  * Call each process' poll handler.
223  */
224 /*---------------------------------------------------------------------------*/
225 static void
226 do_poll(void)
227 {
228  struct process *p;
229 
230  poll_requested = 0;
231  /* Call the processes that needs to be polled. */
232  for(p = process_list; p != NULL; p = p->next) {
233  if(p->needspoll) {
234  p->state = PROCESS_STATE_RUNNING;
235  p->needspoll = 0;
236  call_process(p, PROCESS_EVENT_POLL, NULL);
237  }
238  }
239 }
240 /*---------------------------------------------------------------------------*/
241 /*
242  * Process the next event in the event queue and deliver it to
243  * listening processes.
244  */
245 /*---------------------------------------------------------------------------*/
246 static void
247 do_event(void)
248 {
249  static process_event_t ev;
250  static process_data_t data;
251  static struct process *receiver;
252  static struct process *p;
253 
254  /*
255  * If there are any events in the queue, take the first one and walk
256  * through the list of processes to see if the event should be
257  * delivered to any of them. If so, we call the event handler
258  * function for the process. We only process one event at a time and
259  * call the poll handlers inbetween.
260  */
261 
262  if(nevents > 0) {
263 
264  /* There are events that we should deliver. */
265  ev = events[fevent].ev;
266 
267  data = events[fevent].data;
268  receiver = events[fevent].p;
269 
270  /* Since we have seen the new event, we move pointer upwards
271  and decrese the number of events. */
272  fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS;
273  --nevents;
274 
275  /* If this is a broadcast event, we deliver it to all events, in
276  order of their priority. */
277  if(receiver == PROCESS_BROADCAST) {
278  for(p = process_list; p != NULL; p = p->next) {
279 
280  /* If we have been requested to poll a process, we do this in
281  between processing the broadcast event. */
282  if(poll_requested) {
283  do_poll();
284  }
285  call_process(p, ev, data);
286  }
287  } else {
288  /* This is not a broadcast event, so we deliver it to the
289  specified process. */
290  /* If the event was an INIT event, we should also update the
291  state of the process. */
292  if(ev == PROCESS_EVENT_INIT) {
293  receiver->state = PROCESS_STATE_RUNNING;
294  }
295 
296  /* Make sure that the process actually is running. */
297  call_process(receiver, ev, data);
298  }
299  }
300 }
301 /*---------------------------------------------------------------------------*/
302 int
304 {
305  /* Process poll events. */
306  if(poll_requested) {
307  do_poll();
308  }
309 
310  /* Process one event from the queue */
311  do_event();
312 
313  return nevents + poll_requested;
314 }
315 /*---------------------------------------------------------------------------*/
316 int
318 {
319  return nevents + poll_requested;
320 }
321 /*---------------------------------------------------------------------------*/
322 int
323 process_post(struct process *p, process_event_t ev, process_data_t data)
324 {
325  static process_num_events_t snum;
326  if(PROCESS_CURRENT() == NULL) {
327  PRINTF("process_post: NULL process posts event %d to process '%s', nevents %d\n",
328  ev,PROCESS_NAME_STRING(p), nevents);
329  } else {
330  PRINTF("process_post: Process '%s' posts event %d to process '%s', nevents %d\n",
331  PROCESS_NAME_STRING(PROCESS_CURRENT()), ev,
332  p == PROCESS_BROADCAST? "<broadcast>": PROCESS_NAME_STRING(p), nevents);
333  }
334 
335  if(nevents == PROCESS_CONF_NUMEVENTS) {
336 // #if DEBUG
337  if(p == PROCESS_BROADCAST) {
338  printf("soft panic: event queue is full when broadcast event %d was posted from %s %u\n", ev, PROCESS_NAME_STRING(process_current),nevents);
339  } else {
340  printf("soft panic: event queue is full when event %d was posted to %s frpm %s %u\n", ev, PROCESS_NAME_STRING(p), PROCESS_NAME_STRING(process_current),nevents);
341  }
342 // #endif /* DEBUG */
343  return PROCESS_ERR_FULL;
344  }
345 
346  snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
347  events[snum].ev = ev;
348  events[snum].data = data;
349  events[snum].p = p;
350  ++nevents;
351 
352 #if PROCESS_CONF_STATS
353  if(nevents > process_maxevents) {
354  process_maxevents = nevents;
355  }
356 #endif /* PROCESS_CONF_STATS */
357 
358  return PROCESS_ERR_OK;
359 }
360 /*---------------------------------------------------------------------------*/
361 void
362 process_post_synch(struct process *p, process_event_t ev, process_data_t data)
363 {
364  struct process *caller = process_current;
365 
366  call_process(p, ev, data);
367  process_current = caller;
368 }
369 /*---------------------------------------------------------------------------*/
370 void
371 process_poll(struct process *p)
372 {
373  if(p != NULL) {
374  if(p->state == PROCESS_STATE_RUNNING ||
375  p->state == PROCESS_STATE_CALLED) {
376  p->needspoll = 1;
377  poll_requested = 1;
378  }
379  }
380 }
381 /*---------------------------------------------------------------------------*/
382 int
383 process_is_running(struct process *p)
384 {
385  return p->state != PROCESS_STATE_NONE;
386 }
387 /*---------------------------------------------------------------------------*/
388 /** @} */