Subversion Repositories shark

Rev

Rev 38 | Go to most recent revision | Details | Compare with Previous | 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
 ------------
318 giacomo 23
 CVS :        $Id: conditio.c,v 1.4 2003-11-05 15:05:11 giacomo Exp $
2 pj 24
 
25
 File:        $File$
318 giacomo 26
 Revision:    $Revision: 1.4 $
27
 Last update: $Date: 2003-11-05 15:05:11 $
2 pj 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>
29 pj 62
#include <kernel/iqueue.h>
2 pj 63
 
64
/*---------------------------------------------------------------------*/
65
/* Condition variables                                                 */
66
/*---------------------------------------------------------------------*/
67
 
68
static int condition_once = 1;
69
 
70
/* this is the test that is done when a task is being killed
71
   and it is waiting on a condition */
72
static int condition_cancellation_point(PID i, void *arg)
73
{
74
    LEVEL l;
75
 
76
    if (proc_table[i].status == WAIT_COND) {
77
    /* if the task is waiting on a condition variable, we have to extract it
78
       from the waiters queue, then set the KILL_REQUEST flag, and reinsert
79
       the task into the ready queue so it can reaquire the mutex and die */
29 pj 80
      iq_extract(i,&proc_table[i].cond_waiting->waiters);
81
      if (iq_isempty(&proc_table[i].cond_waiting->waiters))
2 pj 82
        proc_table[i].cond_waiting->used_for_waiting = NULL;
83
      proc_table[i].cond_waiting = NULL;
84
 
85
      l = proc_table[i].task_level;
38 pj 86
      level_table[l]->public_unblock(l,i);
2 pj 87
      /* then, the kill_request flag is set, and when the task is rescheduled
88
         it autokill itself... */
89
 
90
      return 1;
91
    }
92
 
93
    return 0;
94
}
95
 
96
 
97
int cond_init(cond_t *cond)
98
{
99
  /* first, if it is the first time that the cond_init is called,
100
     register the cancellation point */
101
  if (condition_once) {
102
    condition_once = 0;
103
    register_cancellation_point(condition_cancellation_point, NULL);
104
  }
105
 
29 pj 106
  iq_init (&cond->waiters, &freedesc, 0);
107
 
2 pj 108
  cond->used_for_waiting = NULL;
109
 
110
  return 0;
111
}
112
 
113
int cond_destroy(cond_t *cond)
114
{
29 pj 115
  if (!iq_isempty(&cond->waiters))
2 pj 116
    return (EBUSY);
117
 
118
  return 0;
119
}
120
 
121
int cond_signal(cond_t *cond)
122
{
123
  LEVEL l;
124
  PID p;
125
 
126
  proc_table[exec_shadow].context = kern_context_save();
127
 
29 pj 128
  if (!iq_isempty(&cond->waiters)) {
129
    p = iq_getfirst(&cond->waiters);
2 pj 130
 
131
    l = proc_table[p].task_level;
38 pj 132
    level_table[l]->public_unblock(l,p);
2 pj 133
 
134
    scheduler();
135
  }
136
 
137
  kern_context_load(proc_table[exec_shadow].context);
138
  return 0;
139
}
140
 
141
int cond_broadcast(cond_t *cond)
142
{
143
  LEVEL l;
144
  PID p;
145
 
146
  proc_table[exec_shadow].context = kern_context_save();
147
 
29 pj 148
  if (!iq_isempty(&cond->waiters)) {
2 pj 149
    do {
29 pj 150
      p = iq_getfirst(&cond->waiters);
2 pj 151
 
152
      l = proc_table[p].task_level;
38 pj 153
      level_table[l]->public_unblock(l,p);
29 pj 154
    } while(!iq_isempty(&cond->waiters));
2 pj 155
 
156
    scheduler();
157
  }
158
  kern_context_load(proc_table[exec_shadow].context);
159
  return 0;
160
}
161
 
162
int cond_wait(cond_t *cond, mutex_t *mutex)
163
{
164
  LEVEL l;
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
 
38 pj 201
  kern_epilogue_macro();
2 pj 202
 
203
  l = proc_table[exec_shadow].task_level;
38 pj 204
  level_table[l]->public_block(l,exec_shadow);
2 pj 205
 
206
  /* we insert the task in the condition queue */
207
  proc_table[exec_shadow].status = WAIT_COND;
29 pj 208
  iq_priority_insert(exec_shadow,&cond->waiters);
2 pj 209
 
210
  /* then, we set into the processor descriptor the condition on that
211
     the task is blocked... (if the task is killed while it is waiting
212
     on the condition, we have to remove it from the waiters queue, so
213
     we need the condition variable...) */
214
  proc_table[exec_shadow].cond_waiting = cond;
215
 
216
  /* and finally we reschedule */
217
  exec = exec_shadow = -1;
218
  scheduler();
219
  ll_context_to(proc_table[exec_shadow].context);
220
 
221
  /* when we arrive here:
222
     - the task did't die while it was waiting on the condition
223
       (normally, the cancelability state is set to deferred;
224
        if someone kills the task, we have first to lock the mutex,
225
        and then die. Furthermore no cond_signal can be catched by a task
226
        that have to die, so in the makefree function we extract the
227
        task from the waiters queue)
228
     - the task still in the non-preemptive state
229
     - the task have to reaquire the mutex to test again the condition
230
     - the task have to reset the cond_waiting pointer set before
231
  */
232
  if (proc_table[exec_shadow].cond_waiting != NULL) {
233
    proc_table[exec_shadow].cond_waiting = NULL;
234
 
29 pj 235
    if (iq_isempty(&cond->waiters)) cond->used_for_waiting = NULL;
2 pj 236
  }
237
  task_preempt();
238
 
239
  mutex_lock(mutex);
240
  mutex->use--;
241
 
242
  task_testcancel();
243
 
244
  return 0;
245
}
246
 
247
 
248
/* if this event fires the task passed as argument is blocked on a condition
249
   with a cond_timedwait.
250
   We have to:
251
   - extract the task from the waiters queue, because the task has to be
252
     woken up and must not use any cond_signal
253
   - reset the delaytimer...
254
   - call the task-insert...
255
*/
256
void condition_timer(void *arg)
257
{
258
  PID p = (PID)arg;
259
  LEVEL l;
260
 
29 pj 261
  iq_extract(p,&proc_table[p].cond_waiting->waiters);
262
  if (iq_isempty(&proc_table[p].cond_waiting->waiters))
2 pj 263
    proc_table[p].cond_waiting->used_for_waiting = NULL;
264
  proc_table[p].cond_waiting = NULL;
265
 
266
  proc_table[p].delay_timer = -1;
267
 
268
  l = proc_table[p].task_level;
38 pj 269
  level_table[l]->public_unblock(l,p);
2 pj 270
 
271
  event_need_reschedule();
272
}
273
 
274
int cond_timedwait(cond_t *cond, mutex_t *mutex,
275
                   const struct timespec *abstime)
276
{
277
  LEVEL l;
278
  int returnvalue = 0;
279
 
280
  /* Why I used task_nopreempt???... because we have to unlock the mutex,
281
     and we can't call mutex_unlock after kern_context_save (the unlock
282
     could call context_save itself...) */
283
  task_nopreempt();
284
 
285
  /* First, the cond_wait is a cancellation point... */
286
  task_testcancel();
287
 
288
  /* all the task that uses NOW this condition are waiting on the same
289
     mutex??? */
290
  if (cond->used_for_waiting) {
291
    if (cond->used_for_waiting != mutex) {
292
      task_preempt();
293
      return (EINVAL);
294
    }
295
  }
296
  else
297
    cond->used_for_waiting = mutex;
298
 
299
  /* If the task is not canceled with testcancel, we block it now... */
300
 
301
  /* The mutex can't be destroyed while we are waiting on a condition,
302
     so we tell the mutex that a task is using it althought it is not
303
     busy (the cond_wait have to unlock the mutex!!!)... */
304
  mutex->use++;
305
  if (mutex_unlock(mutex)) {
306
    /* some problems unlocking the mutex... */
307
    mutex->use--;
308
    task_preempt();
309
    return (EINVAL);
310
  }
311
 
312
  /* now, we really block the task... */
313
  proc_table[exec_shadow].context = kern_context_save();
314
 
38 pj 315
  kern_epilogue_macro();
2 pj 316
 
317
  l = proc_table[exec_shadow].task_level;
38 pj 318
  level_table[l]->public_block(l,exec_shadow);
2 pj 319
 
320
  /* we insert the task in the condition queue */
321
  proc_table[exec_shadow].status = WAIT_COND;
29 pj 322
  iq_priority_insert(exec_shadow,&cond->waiters);
2 pj 323
 
324
  /* then, we set into the processor descriptor the condition on that
325
     the task is blocked... (if the task is killed while it is waiting
326
     on the condition, we have to remove it from the waiters queue, so
327
     we need the condition variable...) */
328
  proc_table[exec_shadow].cond_waiting = cond;
329
 
330
  /* we can use the delaytimer because if we are here we are not in a
331
     task_delay */
332
  proc_table[exec_shadow].delay_timer =
333
    kern_event_post(abstime,condition_timer,(void *)exec_shadow);
334
 
335
  /* and finally we reschedule */
336
  exec = exec_shadow = -1;
337
  scheduler();
338
  ll_context_to(proc_table[exec_shadow].context);
339
 
340
  if (proc_table[exec_shadow].delay_timer != -1)
38 pj 341
    kern_event_delete(proc_table[exec_shadow].delay_timer);
2 pj 342
 
343
  /* when we arrive here:
344
     - the task did't die while it was waiting on the condition
345
       (normally, the cancelability state is set to deferred;
346
        if someone kills the task, we have first to lock the mutex,
347
        and then die. Furthermore no cond_signal can be catched by a task
348
        that have to die, so in the makefree function we extract the
349
        task from the waiters queue)
350
     - the task still in the non-preemptive state
351
     - the task have to reaquire the mutex to test again the condition
352
     - the task have to reset the cond_waiting pointer set before
353
     Note that cond_wait has to be called with cancelability set to
354
     deferred... so we insert a testcancel after the mutex_lock...
355
  */
356
  if (proc_table[exec_shadow].cond_waiting != NULL) {
357
    proc_table[exec_shadow].cond_waiting = NULL;
358
 
29 pj 359
    if (iq_isempty(&cond->waiters)) cond->used_for_waiting = NULL;
2 pj 360
  }
361
  else
362
    /* cond_waiting == NULL if the task is killed or the timer has fired */
363
    returnvalue = ETIMEDOUT;
364
 
365
  task_preempt();
366
 
367
  mutex_lock(mutex);
368
  mutex->use--;
369
 
370
  task_testcancel();
371
 
372
  return returnvalue;
373
}