Subversion Repositories shark

Rev

Rev 29 | Rev 318 | 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
 ------------
38 pj 23
 CVS :        $Id: conditio.c,v 1.3 2003-01-07 17:07:49 pj Exp $
2 pj 24
 
25
 File:        $File$
38 pj 26
 Revision:    $Revision: 1.3 $
27
 Last update: $Date: 2003-01-07 17:07:49 $
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
  kern_sti();
221
 
222
  /* when we arrive here:
223
     - the task did't die while it was waiting on the condition
224
       (normally, the cancelability state is set to deferred;
225
        if someone kills the task, we have first to lock the mutex,
226
        and then die. Furthermore no cond_signal can be catched by a task
227
        that have to die, so in the makefree function we extract the
228
        task from the waiters queue)
229
     - the task still in the non-preemptive state
230
     - the task have to reaquire the mutex to test again the condition
231
     - the task have to reset the cond_waiting pointer set before
232
  */
233
  if (proc_table[exec_shadow].cond_waiting != NULL) {
234
    proc_table[exec_shadow].cond_waiting = NULL;
235
 
29 pj 236
    if (iq_isempty(&cond->waiters)) cond->used_for_waiting = NULL;
2 pj 237
  }
238
  task_preempt();
239
 
240
  mutex_lock(mutex);
241
  mutex->use--;
242
 
243
  task_testcancel();
244
 
245
  return 0;
246
}
247
 
248
 
249
/* if this event fires the task passed as argument is blocked on a condition
250
   with a cond_timedwait.
251
   We have to:
252
   - extract the task from the waiters queue, because the task has to be
253
     woken up and must not use any cond_signal
254
   - reset the delaytimer...
255
   - call the task-insert...
256
*/
257
void condition_timer(void *arg)
258
{
259
  PID p = (PID)arg;
260
  LEVEL l;
261
 
29 pj 262
  iq_extract(p,&proc_table[p].cond_waiting->waiters);
263
  if (iq_isempty(&proc_table[p].cond_waiting->waiters))
2 pj 264
    proc_table[p].cond_waiting->used_for_waiting = NULL;
265
  proc_table[p].cond_waiting = NULL;
266
 
267
  proc_table[p].delay_timer = -1;
268
 
269
  l = proc_table[p].task_level;
38 pj 270
  level_table[l]->public_unblock(l,p);
2 pj 271
 
272
  event_need_reschedule();
273
}
274
 
275
int cond_timedwait(cond_t *cond, mutex_t *mutex,
276
                   const struct timespec *abstime)
277
{
278
  LEVEL l;
279
  int returnvalue = 0;
280
 
281
  /* Why I used task_nopreempt???... because we have to unlock the mutex,
282
     and we can't call mutex_unlock after kern_context_save (the unlock
283
     could call context_save itself...) */
284
  task_nopreempt();
285
 
286
  /* First, the cond_wait is a cancellation point... */
287
  task_testcancel();
288
 
289
  /* all the task that uses NOW this condition are waiting on the same
290
     mutex??? */
291
  if (cond->used_for_waiting) {
292
    if (cond->used_for_waiting != mutex) {
293
      task_preempt();
294
      return (EINVAL);
295
    }
296
  }
297
  else
298
    cond->used_for_waiting = mutex;
299
 
300
  /* If the task is not canceled with testcancel, we block it now... */
301
 
302
  /* The mutex can't be destroyed while we are waiting on a condition,
303
     so we tell the mutex that a task is using it althought it is not
304
     busy (the cond_wait have to unlock the mutex!!!)... */
305
  mutex->use++;
306
  if (mutex_unlock(mutex)) {
307
    /* some problems unlocking the mutex... */
308
    mutex->use--;
309
    task_preempt();
310
    return (EINVAL);
311
  }
312
 
313
  /* now, we really block the task... */
314
  proc_table[exec_shadow].context = kern_context_save();
315
 
38 pj 316
  kern_epilogue_macro();
2 pj 317
 
318
  l = proc_table[exec_shadow].task_level;
38 pj 319
  level_table[l]->public_block(l,exec_shadow);
2 pj 320
 
321
  /* we insert the task in the condition queue */
322
  proc_table[exec_shadow].status = WAIT_COND;
29 pj 323
  iq_priority_insert(exec_shadow,&cond->waiters);
2 pj 324
 
325
  /* then, we set into the processor descriptor the condition on that
326
     the task is blocked... (if the task is killed while it is waiting
327
     on the condition, we have to remove it from the waiters queue, so
328
     we need the condition variable...) */
329
  proc_table[exec_shadow].cond_waiting = cond;
330
 
331
  /* we can use the delaytimer because if we are here we are not in a
332
     task_delay */
333
  proc_table[exec_shadow].delay_timer =
334
    kern_event_post(abstime,condition_timer,(void *)exec_shadow);
335
 
336
  /* and finally we reschedule */
337
  exec = exec_shadow = -1;
338
  scheduler();
339
  ll_context_to(proc_table[exec_shadow].context);
340
 
341
  if (proc_table[exec_shadow].delay_timer != -1)
38 pj 342
    kern_event_delete(proc_table[exec_shadow].delay_timer);
2 pj 343
 
344
  kern_sti();
345
 
346
  /* when we arrive here:
347
     - the task did't die while it was waiting on the condition
348
       (normally, the cancelability state is set to deferred;
349
        if someone kills the task, we have first to lock the mutex,
350
        and then die. Furthermore no cond_signal can be catched by a task
351
        that have to die, so in the makefree function we extract the
352
        task from the waiters queue)
353
     - the task still in the non-preemptive state
354
     - the task have to reaquire the mutex to test again the condition
355
     - the task have to reset the cond_waiting pointer set before
356
     Note that cond_wait has to be called with cancelability set to
357
     deferred... so we insert a testcancel after the mutex_lock...
358
  */
359
  if (proc_table[exec_shadow].cond_waiting != NULL) {
360
    proc_table[exec_shadow].cond_waiting = NULL;
361
 
29 pj 362
    if (iq_isempty(&cond->waiters)) cond->used_for_waiting = NULL;
2 pj 363
  }
364
  else
365
    /* cond_waiting == NULL if the task is killed or the timer has fired */
366
    returnvalue = ETIMEDOUT;
367
 
368
  task_preempt();
369
 
370
  mutex_lock(mutex);
371
  mutex->use--;
372
 
373
  task_testcancel();
374
 
375
  return returnvalue;
376
}