Subversion Repositories shark

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 pj 1
/*
2
 * Project: S.Ha.R.K.
3
 *
4
 * Coordinators:
5
 *   Giorgio Buttazzo    <giorgio@sssup.it>
6
 *   Paolo Gai           <pj@gandalf.sssup.it>
7
 *
8
 * Authors     :
9
 *   Paolo Gai           <pj@gandalf.sssup.it>
10
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
11
 *   Luca Abeni          <luca@gandalf.sssup.it>
12
 *   (see the web pages for full authors list)
13
 *
14
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
15
 *
16
 * http://www.sssup.it
17
 * http://retis.sssup.it
18
 * http://shark.sssup.it
19
 */
20
 
21
/**
22
 ------------
23
 CVS :        $Id: conditio.c,v 1.1.1.1 2002-03-29 14:12:51 pj Exp $
24
 
25
 File:        $File$
26
 Revision:    $Revision: 1.1.1.1 $
27
 Last update: $Date: 2002-03-29 14:12:51 $
28
 ------------
29
 
30
This file contains the condition variables handling functions.
31
 
32
**/
33
 
34
/*
35
 * Copyright (C) 2000 Paolo Gai
36
 *
37
 * This program is free software; you can redistribute it and/or modify
38
 * it under the terms of the GNU General Public License as published by
39
 * the Free Software Foundation; either version 2 of the License, or
40
 * (at your option) any later version.
41
 *
42
 * This program is distributed in the hope that it will be useful,
43
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
44
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45
 * GNU General Public License for more details.
46
 *
47
 * You should have received a copy of the GNU General Public License
48
 * along with this program; if not, write to the Free Software
49
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
50
 *
51
 */
52
 
53
 
54
 
55
#include <kernel/const.h>
56
#include <sys/types.h>
57
#include <kernel/model.h>
58
#include <kernel/descr.h>
59
#include <kernel/var.h>
60
#include <kernel/func.h>
61
#include <errno.h>
62
 
63
/*---------------------------------------------------------------------*/
64
/* Condition variables                                                 */
65
/*---------------------------------------------------------------------*/
66
 
67
static int condition_once = 1;
68
 
69
/* this is the test that is done when a task is being killed
70
   and it is waiting on a condition */
71
static int condition_cancellation_point(PID i, void *arg)
72
{
73
    LEVEL l;
74
 
75
    if (proc_table[i].status == WAIT_COND) {
76
    /* if the task is waiting on a condition variable, we have to extract it
77
       from the waiters queue, then set the KILL_REQUEST flag, and reinsert
78
       the task into the ready queue so it can reaquire the mutex and die */
79
      q_extract(i,&proc_table[i].cond_waiting->waiters);
80
      if (proc_table[i].cond_waiting->waiters == NIL)
81
        proc_table[i].cond_waiting->used_for_waiting = NULL;
82
      proc_table[i].cond_waiting = NULL;
83
 
84
      l = proc_table[i].task_level;
85
      level_table[l]->task_insert(l,i);
86
      /* then, the kill_request flag is set, and when the task is rescheduled
87
         it autokill itself... */
88
 
89
      return 1;
90
    }
91
 
92
    return 0;
93
}
94
 
95
 
96
int cond_init(cond_t *cond)
97
{
98
  /* first, if it is the first time that the cond_init is called,
99
     register the cancellation point */
100
  if (condition_once) {
101
    condition_once = 0;
102
    register_cancellation_point(condition_cancellation_point, NULL);
103
  }
104
 
105
  cond->waiters = NIL;
106
  cond->used_for_waiting = NULL;
107
 
108
  return 0;
109
}
110
 
111
int cond_destroy(cond_t *cond)
112
{
113
  if (cond->waiters != NIL)
114
    return (EBUSY);
115
 
116
  return 0;
117
}
118
 
119
int cond_signal(cond_t *cond)
120
{
121
  LEVEL l;
122
  PID p;
123
 
124
  proc_table[exec_shadow].context = kern_context_save();
125
 
126
  if (cond->waiters != NIL) {
127
    p = q_getfirst(&cond->waiters);
128
 
129
    l = proc_table[p].task_level;
130
    level_table[l]->task_insert(l,p);
131
 
132
    scheduler();
133
  }
134
 
135
  kern_context_load(proc_table[exec_shadow].context);
136
  return 0;
137
}
138
 
139
int cond_broadcast(cond_t *cond)
140
{
141
  LEVEL l;
142
  PID p;
143
 
144
  proc_table[exec_shadow].context = kern_context_save();
145
 
146
  if (cond->waiters != NIL) {
147
    do {
148
      p = q_getfirst(&cond->waiters);
149
 
150
      l = proc_table[p].task_level;
151
      level_table[l]->task_insert(l,p);
152
    } while(cond->waiters != NIL);
153
 
154
    scheduler();
155
  }
156
  kern_context_load(proc_table[exec_shadow].context);
157
  return 0;
158
}
159
 
160
int cond_wait(cond_t *cond, mutex_t *mutex)
161
{
162
  LEVEL l;
163
  struct timespec ty;
164
  TIME tx;
165
 
166
  /* Why I used task_nopreempt???... because we have to unlock the mutex,
167
     and we can't call mutex_unlock after kern_context_save (the unlock
168
     could call context_save itself...) */
169
  task_nopreempt();
170
 
171
  /* First, the cond_wait is a cancellation point... */
172
  task_testcancel();
173
 
174
  /* all the task that uses NOW this condition are waiting on the same
175
     mutex??? */
176
  if (cond->used_for_waiting) {
177
    if (cond->used_for_waiting != mutex) {
178
      task_preempt();
179
      return (EINVAL);
180
    }
181
  }
182
  else
183
    cond->used_for_waiting = mutex;
184
 
185
  /* If the task is not canceled with testcancel, we block it now... */
186
 
187
  /* The mutex can't be destroyed while we are waiting on a condition,
188
     so we tell the mutex that a task is using it althought it is not
189
     busy (the cond_wait have to unlock the mutex!!!)... */
190
  mutex->use++;
191
  if (mutex_unlock(mutex)) {
192
    /* some problems unlocking the mutex... */
193
    mutex->use--;
194
    task_preempt();
195
    return (EINVAL);
196
  }
197
 
198
  /* now, we really block the task... */
199
  proc_table[exec_shadow].context = kern_context_save();
200
 
201
  /* SAME AS SCHEDULER... manage the capacity event and the load_info */
202
  ll_gettime(TIME_EXACT, &schedule_time);
203
  SUBTIMESPEC(&schedule_time, &cap_lasttime, &ty);
204
  tx = TIMESPEC2USEC(&ty);
205
  proc_table[exec_shadow].avail_time -= tx;
206
  jet_update_slice(tx);
207
  if (cap_timer != NIL) {
208
    event_delete(cap_timer);
209
    cap_timer = NIL;
210
  }
211
 
212
  l = proc_table[exec_shadow].task_level;
213
  level_table[l]->task_extract(l,exec_shadow);
214
 
215
  /* we insert the task in the condition queue */
216
  proc_table[exec_shadow].status = WAIT_COND;
217
  q_insert(exec_shadow,&cond->waiters);
218
 
219
  /* then, we set into the processor descriptor the condition on that
220
     the task is blocked... (if the task is killed while it is waiting
221
     on the condition, we have to remove it from the waiters queue, so
222
     we need the condition variable...) */
223
  proc_table[exec_shadow].cond_waiting = cond;
224
 
225
  /* and finally we reschedule */
226
  exec = exec_shadow = -1;
227
  scheduler();
228
  ll_context_to(proc_table[exec_shadow].context);
229
  kern_sti();
230
 
231
  /* when we arrive here:
232
     - the task did't die while it was waiting on the condition
233
       (normally, the cancelability state is set to deferred;
234
        if someone kills the task, we have first to lock the mutex,
235
        and then die. Furthermore no cond_signal can be catched by a task
236
        that have to die, so in the makefree function we extract the
237
        task from the waiters queue)
238
     - the task still in the non-preemptive state
239
     - the task have to reaquire the mutex to test again the condition
240
     - the task have to reset the cond_waiting pointer set before
241
  */
242
  if (proc_table[exec_shadow].cond_waiting != NULL) {
243
    proc_table[exec_shadow].cond_waiting = NULL;
244
 
245
    if (cond->waiters == NIL) cond->used_for_waiting = NULL;
246
  }
247
  task_preempt();
248
 
249
  mutex_lock(mutex);
250
  mutex->use--;
251
 
252
  task_testcancel();
253
 
254
  return 0;
255
}
256
 
257
 
258
/* if this event fires the task passed as argument is blocked on a condition
259
   with a cond_timedwait.
260
   We have to:
261
   - extract the task from the waiters queue, because the task has to be
262
     woken up and must not use any cond_signal
263
   - reset the delaytimer...
264
   - call the task-insert...
265
*/
266
void condition_timer(void *arg)
267
{
268
  PID p = (PID)arg;
269
  LEVEL l;
270
 
271
  q_extract(p,&proc_table[p].cond_waiting->waiters);
272
  if (proc_table[p].cond_waiting->waiters == NIL)
273
    proc_table[p].cond_waiting->used_for_waiting = NULL;
274
  proc_table[p].cond_waiting = NULL;
275
 
276
  proc_table[p].delay_timer = -1;
277
 
278
  l = proc_table[p].task_level;
279
  level_table[l]->task_insert(l,p);
280
 
281
  event_need_reschedule();
282
}
283
 
284
int cond_timedwait(cond_t *cond, mutex_t *mutex,
285
                   const struct timespec *abstime)
286
{
287
  LEVEL l;
288
  int returnvalue = 0;
289
  struct timespec ty;
290
  TIME tx;
291
 
292
  /* Why I used task_nopreempt???... because we have to unlock the mutex,
293
     and we can't call mutex_unlock after kern_context_save (the unlock
294
     could call context_save itself...) */
295
  task_nopreempt();
296
 
297
  /* First, the cond_wait is a cancellation point... */
298
  task_testcancel();
299
 
300
  /* all the task that uses NOW this condition are waiting on the same
301
     mutex??? */
302
  if (cond->used_for_waiting) {
303
    if (cond->used_for_waiting != mutex) {
304
      task_preempt();
305
      return (EINVAL);
306
    }
307
  }
308
  else
309
    cond->used_for_waiting = mutex;
310
 
311
  /* If the task is not canceled with testcancel, we block it now... */
312
 
313
  /* The mutex can't be destroyed while we are waiting on a condition,
314
     so we tell the mutex that a task is using it althought it is not
315
     busy (the cond_wait have to unlock the mutex!!!)... */
316
  mutex->use++;
317
  if (mutex_unlock(mutex)) {
318
    /* some problems unlocking the mutex... */
319
    mutex->use--;
320
    task_preempt();
321
    return (EINVAL);
322
  }
323
 
324
  /* now, we really block the task... */
325
  proc_table[exec_shadow].context = kern_context_save();
326
 
327
  /* SAME AS SCHEDULER... manage the capacity event and the load_info */
328
  ll_gettime(TIME_EXACT, &schedule_time);
329
  SUBTIMESPEC(&schedule_time, &cap_lasttime, &ty);
330
  tx = TIMESPEC2USEC(&ty);
331
  proc_table[exec_shadow].avail_time -= tx;
332
  jet_update_slice(tx);
333
  if (cap_timer != NIL) {
334
    event_delete(cap_timer);
335
    cap_timer = NIL;
336
  }
337
 
338
  l = proc_table[exec_shadow].task_level;
339
  level_table[l]->task_extract(l,exec_shadow);
340
 
341
  /* we insert the task in the condition queue */
342
  proc_table[exec_shadow].status = WAIT_COND;
343
  q_insert(exec_shadow,&cond->waiters);
344
 
345
  /* then, we set into the processor descriptor the condition on that
346
     the task is blocked... (if the task is killed while it is waiting
347
     on the condition, we have to remove it from the waiters queue, so
348
     we need the condition variable...) */
349
  proc_table[exec_shadow].cond_waiting = cond;
350
 
351
  /* we can use the delaytimer because if we are here we are not in a
352
     task_delay */
353
  proc_table[exec_shadow].delay_timer =
354
    kern_event_post(abstime,condition_timer,(void *)exec_shadow);
355
 
356
  /* and finally we reschedule */
357
  exec = exec_shadow = -1;
358
  scheduler();
359
  ll_context_to(proc_table[exec_shadow].context);
360
 
361
  if (proc_table[exec_shadow].delay_timer != -1)
362
    event_delete(proc_table[exec_shadow].delay_timer);
363
 
364
  kern_sti();
365
 
366
  /* when we arrive here:
367
     - the task did't die while it was waiting on the condition
368
       (normally, the cancelability state is set to deferred;
369
        if someone kills the task, we have first to lock the mutex,
370
        and then die. Furthermore no cond_signal can be catched by a task
371
        that have to die, so in the makefree function we extract the
372
        task from the waiters queue)
373
     - the task still in the non-preemptive state
374
     - the task have to reaquire the mutex to test again the condition
375
     - the task have to reset the cond_waiting pointer set before
376
     Note that cond_wait has to be called with cancelability set to
377
     deferred... so we insert a testcancel after the mutex_lock...
378
  */
379
  if (proc_table[exec_shadow].cond_waiting != NULL) {
380
    proc_table[exec_shadow].cond_waiting = NULL;
381
 
382
    if (cond->waiters == NIL) cond->used_for_waiting = NULL;
383
  }
384
  else
385
    /* cond_waiting == NULL if the task is killed or the timer has fired */
386
    returnvalue = ETIMEDOUT;
387
 
388
  task_preempt();
389
 
390
  mutex_lock(mutex);
391
  mutex->use--;
392
 
393
  task_testcancel();
394
 
395
  return returnvalue;
396
}