Subversion Repositories shark

Rev

Rev 29 | 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: sem.c,v 1.3 2003-01-07 17:07:51 pj Exp $
2 pj 24
 
25
 File:        $File$
38 pj 26
 Revision:    $Revision: 1.3 $
27
 Last update: $Date: 2003-01-07 17:07:51 $
2 pj 28
 ------------
29
 
30
 This file contains the Hartik 3.3.1 Semaphore functions
31
 
32
 Author:      Giuseppe Lipari
33
 
34
 Semaphores:
35
 this is the generalized version of the primitives signal & wait
36
 In this case, the user can specify the number to inc/dec the
37
 semaphore's counter. It is useful in the buffer management
38
 (see port section)
39
 
40
**/
41
 
42
/*
43
 * Copyright (C) 2000 Paolo Gai
44
 *
45
 * This program is free software; you can redistribute it and/or modify
46
 * it under the terms of the GNU General Public License as published by
47
 * the Free Software Foundation; either version 2 of the License, or
48
 * (at your option) any later version.
49
 *
50
 * This program is distributed in the hope that it will be useful,
51
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
52
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
53
 * GNU General Public License for more details.
54
 *
55
 * You should have received a copy of the GNU General Public License
56
 * along with this program; if not, write to the Free Software
57
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
58
 *
59
 */
60
 
61
 
62
#include <stdarg.h>
63
#include <modules/sem.h>
64
#include <kernel/config.h>
65
#include <ll/ll.h>
66
#include <ll/string.h>
67
#include <kernel/const.h>
68
#include <sys/types.h>
69
#include <kernel/descr.h>
70
#include <kernel/var.h>
71
#include <kernel/func.h>
72
#include <kernel/trace.h>
73
#include <limits.h>
74
#include <fcntl.h>
75
 
76
 
77
/* Semaphores descriptor tables */
78
static struct sem_des {
79
    char *name;     /* a name, for named semaphores */
80
    int index;      /* an index for sem_open, containing the sem number */
81
    int count;      /* the semaphore counter */
29 pj 82
    IQUEUE blocked; /* the blocked processes queue */
2 pj 83
    int next;       /* the semaphore queue */
84
    BYTE used;      /* 1 if the semaphore is used */
85
} sem_table[SEM_NSEMS_MAX];
86
 
87
 
88
/* this -IS- an extension to the proc_table!!! */
89
static struct {
90
    int decsem;   /* the value required in sem_xwait */
91
    int sem;      /* the semaphore on whitch the process is blocked */
92
} sp_table[MAX_PROC];
93
 
29 pj 94
static int free_sem;         /* Queue of free sem                    */
2 pj 95
 
96
 
97
 
98
/*----------------------------------------------------------------------*/
99
/* Cancellation test for semaphores                                     */
100
/*----------------------------------------------------------------------*/
101
 
102
/* this is the test that is done when a task is being killed
103
   and it is waiting on a sigwait */
104
static int semwait_cancellation_point(PID i, void *arg)
105
{
106
    LEVEL l;
107
 
108
    if (proc_table[i].status == WAIT_SEM) {
109
      /* the task that have to be killed is waiting on a sig_wait.
110
         we reset the data structures set in sig_wait and then when the
111
         task will return on the sig_wait it will fall into a
112
         task_testcancel */
113
 
114
      /* extract the process from the semaphore queue... */
29 pj 115
      iq_extract(i,&sem_table[ sp_table[i].sem ].blocked);
2 pj 116
 
117
      l = proc_table[i].task_level;
38 pj 118
      level_table[l]->public_unblock(l,i);
2 pj 119
 
120
      return 1;
121
    }
122
 
123
    return 0;
124
}
125
 
126
/*----------------------------------------------------------------------*/
127
/* Init the semaphoric structures                                       */
128
/*----------------------------------------------------------------------*/
129
void SEM_register_module(void)
130
{
131
    int i;
132
 
133
    for (i = 0; i < SEM_NSEMS_MAX; i++) {
134
        sem_table[i].name = NULL;
135
        sem_table[i].index = i;
136
        sem_table[i].count = 0;
29 pj 137
        iq_init(&sem_table[i].blocked, &freedesc, 0);
2 pj 138
        sem_table[i].next = i+1;
139
        sem_table[i].used = 0;
140
    }
141
    sem_table[SEM_NSEMS_MAX-1].next = NIL;
142
    free_sem = 0;
143
 
144
    register_cancellation_point(semwait_cancellation_point, NULL);
145
}
146
 
147
/*----------------------------------------------------------------------*/
148
/* Allocate a semaphoric descriptor and sets the counter to n           */
149
/*----------------------------------------------------------------------*/
150
 
151
// the pshared parameter is NRQ for PSE52
152
int sem_init(sem_t *sem, int pshared, unsigned int value)
153
{
154
    if (value > SEM_VALUE_MAX)
155
      return EINVAL;
156
 
157
    kern_cli();
158
    *sem = free_sem;
159
    if (*sem != NIL) {
160
        free_sem = sem_table[*sem].next;
161
        sem_table[*sem].name = NULL;
162
        sem_table[*sem].count = value;
29 pj 163
        iq_init(&sem_table[*sem].blocked, &freedesc, 0);
2 pj 164
        sem_table[*sem].used = 1;
165
    }
166
    else {
167
        errno = ENOSPC;
168
        kern_sti();
169
        return -1;
170
    }
171
    kern_sti();
172
    return 0;
173
}
174
 
175
/*----------------------------------------------------------------------*/
176
/* Frees a semaphores descriptor                                        */
177
/*----------------------------------------------------------------------*/
178
int sem_destroy(sem_t *sem)
179
{
180
    kern_cli();
181
 
182
    if (*sem < 0 || *sem >= SEM_NSEMS_MAX || !sem_table[*sem].used) {
183
      errno = EINVAL;
184
      kern_sti();
185
      return -1;
186
    }
187
 
188
    if (sem_table[*sem].blocked.first != NIL) {
189
      errno = EBUSY;
190
      kern_sti();
191
      return -1;
192
    }
193
 
194
    sem_table[*sem].used = 0;
195
    sem_table[*sem].next = free_sem;
196
    free_sem = *sem;
197
 
198
    kern_sti();
199
    return 0;
200
}
201
 
202
/*----------------------------------------------------------------------*/
203
/* Allocate a named semaphore                                           */
204
/*----------------------------------------------------------------------*/
205
 
206
// the pshared parameter is NRQ for PSE52
207
sem_t *sem_open(const char *name, int oflag, ...)
208
{
209
    int i, j;
210
    int found = 0;
211
    mode_t m;
212
    sem_t sem;
213
 
214
    kern_cli();
215
 
216
    for (i = 0; i < SEM_NSEMS_MAX; i++)
217
      if (sem_table[i].used) {
218
        if (strcmp(name, sem_table[i].name) == 0) {
219
          found = 1;
220
          break;
221
        }
222
      }
223
    if (found) {
224
      if (oflag == (O_CREAT | O_EXCL)) {
225
          errno = EEXIST;
226
          kern_sti();
227
          return SEM_FAILED;
228
      } else {
229
          kern_sti();
230
          return &sem_table[i].index;
231
      }
232
    } else {
233
      if (!(oflag & O_CREAT)) {
234
          errno = ENOENT;
235
          kern_sti();
236
          return SEM_FAILED;
237
      } else {
238
          va_list l;
239
 
240
          va_start(l, oflag);
241
            m = va_arg(l,mode_t);
242
            j = va_arg(l, int);
243
          va_end(l);
244
 
245
          if (j > SEM_VALUE_MAX) {
246
            errno = EINVAL;
247
            kern_sti();
248
            return SEM_FAILED;
249
          }
250
 
251
          sem = free_sem;
252
          if (sem != -1) {
253
            free_sem = sem_table[sem].next;
254
            sem_table[sem].name = kern_alloc(strlen((char *)name)+1);
255
            strcpy(sem_table[sem].name, (char *)name);
256
            sem_table[sem].count = j;
29 pj 257
            iq_init(&sem_table[sem].blocked, &freedesc, 0);
2 pj 258
            sem_table[sem].used = 1;
259
            kern_sti();
260
            return &sem_table[sem].index;
261
          }
262
          else {
263
            errno = ENOSPC;
264
            kern_sti();
265
            return SEM_FAILED;
266
          }
267
      }
268
    }
269
}
270
 
271
/*----------------------------------------------------------------------*/
272
/* Frees a named semaphore                                              */
273
/*----------------------------------------------------------------------*/
274
int sem_close(sem_t *sem)
275
{
276
    kern_cli();
277
 
278
    if (*sem < 0 || *sem >= SEM_NSEMS_MAX || !sem_table[*sem].used) {
279
      errno = EINVAL;
280
      kern_sti();
281
      return -1;
282
    }
283
 
284
/*  why not???
285
    if (sem_table[*sem].q_first != -1) {
286
      errno = EBUSY;
287
      kern_sti();
288
      return -1;
289
    } */
290
 
291
    kern_free(sem_table[*sem].name,strlen(sem_table[*sem].name)+1);
292
    sem_table[*sem].used = 0;
293
    sem_table[*sem].next = free_sem;
294
    free_sem = *sem;
295
 
296
    kern_sti();
297
    return 0;
298
}
299
 
300
/*----------------------------------------------------------------------*/
301
/* Unlink a named semaphore                                             */
302
/*----------------------------------------------------------------------*/
303
int sem_unlink(const char *name)
304
{
305
    int i;
306
    int found = 0;
307
 
308
    kern_cli();
309
 
310
    for (i = 0; i < SEM_NSEMS_MAX; i++)
311
      if (sem_table[i].used) {
312
        if (strcmp(name, sem_table[i].name) == 0) {
313
          found = 1;
314
        }
315
      }
316
 
317
    if (found) {
318
      kern_free(sem_table[i].name,strlen((char *)name)+1);
319
      sem_table[i].used = 0;
320
      sem_table[i].next = free_sem;
321
      free_sem = i;
322
      kern_sti();
323
      return 0;
324
    } else {
325
      errno = ENOENT;
326
      kern_sti();
327
      return SEM_FAILED;
328
    }
329
}
330
 
331
/*----------------------------------------------------------------------*/
332
/* Generic wait. If it is possible, decrements the sem counter of n,    */
333
/* else blocks the task.                                                */
334
/*----------------------------------------------------------------------*/
335
int sem_wait(sem_t *s)
336
{
337
    struct sem_des *s1; /* It speeds up access */
338
 
339
    if (*s < 0 || *s >= SEM_NSEMS_MAX || !sem_table[*s].used) {
340
      errno = EINVAL;
341
      return -1;
342
    }
343
 
344
    task_testcancel();
345
 
346
    proc_table[exec_shadow].context = kern_context_save();
347
 
348
    s1 = &sem_table[*s];
349
 
350
    if (s1->blocked.first != NIL || s1->count == 0)  {
351
            /* We must block exec task   */
352
            LEVEL l;            /* for readableness only */
353
 
354
            /* tracer stuff */
355
            trc_logevent(TRC_SEM_WAIT,s);
356
 
38 pj 357
            kern_epilogue_macro();
358
 
2 pj 359
            l = proc_table[exec_shadow].task_level;
38 pj 360
            level_table[l]->public_block(l,exec_shadow);
2 pj 361
 
362
            /* we insert the task in the semaphore queue */
363
            proc_table[exec_shadow].status = WAIT_SEM;
364
 
365
            /* Prepare sem_table des... */
366
            sp_table[exec_shadow].decsem = 1;
367
            sp_table[exec_shadow].sem = *s;
368
 
369
            /* ...and put it in sem queue */
29 pj 370
            iq_insertlast(exec_shadow,&s1->blocked);
2 pj 371
 
372
            /* and finally we reschedule */
373
            exec = exec_shadow = -1;
374
            scheduler();
375
            kern_context_load(proc_table[exec_shadow].context);
376
 
377
            /* sem_wait is a cancellation point... */
378
            task_testcancel();
379
    }
380
    else {
381
            s1->count--;
382
            /* tracer stuff */
383
            trc_logevent(TRC_SEM_WAIT,s);
384
            kern_context_load(proc_table[exec_shadow].context);
385
    }
386
 
387
    return 0;
388
}
389
 
390
/*----------------------------------------------------------------------*/
391
/* Non-blocking wait                                                    */
392
/*----------------------------------------------------------------------*/
393
int sem_trywait(sem_t *s)
394
{
395
    struct sem_des *s1; /* It speeds up access */
396
 
397
    if (*s < 0 || *s >= SEM_NSEMS_MAX || !sem_table[*s].used) {
398
      errno = EINVAL;
399
      return -1;
400
    }
401
 
402
    kern_cli();
403
 
404
    s1 = &sem_table[*s];
405
 
406
    /* tracer stuff */
407
    //trc_logevent(TRC_SEM_WAITNB,s);
408
 
409
    if (s1->blocked.first != NIL || s1->count == 0)  {
410
      errno = EAGAIN;
411
      kern_sti();
412
      return -1;
413
    }
414
    else
415
      s1->count--;
416
 
417
    kern_sti();
418
    return 0;
419
}
420
 
421
 
422
/*----------------------------------------------------------------------*/
423
/* Generic wait. If it is possible, decrements the sem counter of n,    */
424
/* else blocks the task.                                                */
425
/*----------------------------------------------------------------------*/
426
int sem_xwait(sem_t *s, int n, int wait)
427
{
428
    struct sem_des *s1; /* It speeds up access */
429
 
430
    if (*s < 0 || *s >= SEM_NSEMS_MAX || !sem_table[*s].used) {
431
      errno = EINVAL;
432
      return -1;
433
    }
434
 
435
    /* We do not need to save context if we are sure we shall not block! */
436
    if (wait == NON_BLOCK)
437
      kern_cli();
438
    else
439
      proc_table[exec_shadow].context = kern_context_save();
440
 
441
    s1 = &sem_table[*s];
442
 
443
    /* The non blocking wait is really simple! */
444
    /* We do not suspend or schedule anything  */    
445
    if (wait == NON_BLOCK) {
446
      /* tracer */
447
      //trc_logevent(TRC_SEM_WAITNB,s);
448
 
449
      if (s1->blocked.first != NIL || s1->count < n)  {
450
        errno = EAGAIN;
451
        kern_sti();
452
        return -1;
453
      }
454
      else
455
        s1->count -= n;
456
 
457
      kern_sti();
458
      return 0;
459
    }
460
    /* The blocking wait is more complex... */
461
    else {
462
        /* the blocking wait is a cancellation point */
463
        task_testcancel();
464
 
465
        if (s1->blocked.first != NIL || s1->count < n)  {
466
                    /* We must block exec task   */
467
                    LEVEL l;            /* for readableness only */
468
 
469
                    /* tracer */
470
                    trc_logevent(TRC_SEM_WAIT,s);
38 pj 471
 
472
                    kern_epilogue_macro();
2 pj 473
 
474
                    l = proc_table[exec_shadow].task_level;
38 pj 475
                    level_table[l]->public_block(l,exec_shadow);
2 pj 476
 
477
                    /* we insert the task in the semaphore queue */
478
                    proc_table[exec_shadow].status = WAIT_SEM;
479
 
480
                    /* Prepare sem_table des... */
481
                    sp_table[exec_shadow].decsem = n;
482
                    sp_table[exec_shadow].sem = *s;
483
 
484
                    /* ...and put it in sem queue */
29 pj 485
                    iq_insertlast(exec_shadow,&s1->blocked);
2 pj 486
 
487
                    /* and finally we reschedule */
488
                    exec = exec_shadow = -1;
489
                    scheduler();
490
                    kern_context_load(proc_table[exec_shadow].context);
491
 
492
                    /* sem_wait is a cancellation point... */
493
                    task_testcancel();
494
            }
495
            else {
496
                    s1->count -= n;
497
                    /* tracer */
498
                    trc_logevent(TRC_SEM_WAIT,s);
499
                    kern_context_load(proc_table[exec_shadow].context);
500
            }
501
    }
502
 
503
    return 0;
504
}
505
 
506
/*----------------------------------------------------------------------*/
507
/* Generic signal. It increments the sem counter of 1, and wakes one    */
508
/* of the tasks that are blocked on the semaphore, if it is possible.The*/
509
/* semaphoric queue is a FIFO queue, in order to eliminate deadlocks    */
510
/*----------------------------------------------------------------------*/
511
int sem_post(sem_t *s)
512
{
513
    struct sem_des *s1;        /* it speeds up access          */
514
    int p;                     /* idem                         */
515
    LEVEL l;
516
 
517
    if (*s < 0 || *s >= SEM_NSEMS_MAX || !sem_table[*s].used) {
518
      errno = EINVAL;
519
      return -1;
520
    }
521
 
522
    // ugly patch to call a sem_post!!!
523
    if (ll_ActiveInt()) {
524
      SYS_FLAGS f;
525
      f = kern_fsave();
526
      s1 = &sem_table[*s];
527
      s1->count ++;              /* inc sem count                */
528
 
529
      p = s1->blocked.first;
530
      if (p != NIL && sp_table[p].decsem <= s1->count) {
531
        /* Dec sem count */
532
        s1->count -= sp_table[p].decsem;
533
 
534
        /* Get task from blocked queue */
29 pj 535
        iq_extract(p,&s1->blocked);
2 pj 536
 
537
        l = proc_table[p].task_level;
38 pj 538
        level_table[l]->public_unblock(l,p);
2 pj 539
        /* only a task can be awaken */
540
        /* Preempt if necessary */
541
        event_need_reschedule();
542
      }
543
 
544
      /* tracer */
545
      trc_logevent(TRC_SEM_SIGNAL,s);
546
      kern_frestore(f);
547
    }
548
    else {
549
      proc_table[exec].context = kern_context_save();
550
 
551
      s1 = &sem_table[*s];
552
      s1->count ++;              /* inc sem count                */
553
 
554
      p = s1->blocked.first;
555
      if (p != NIL && sp_table[p].decsem <= s1->count) {
556
        /* Dec sem count */
557
        s1->count -= sp_table[p].decsem;
558
 
559
        /* Get task from blocked queue */
29 pj 560
        iq_extract(p,&s1->blocked);
2 pj 561
 
562
        l = proc_table[p].task_level;
38 pj 563
        level_table[l]->public_unblock(l,p);
2 pj 564
        /* only a task can be awaken */
565
        /* Preempt if necessary */
566
        scheduler();
567
      }
568
 
569
      /* tracer */
570
      trc_logevent(TRC_SEM_SIGNAL,s);
571
 
572
      kern_context_load(proc_table[exec_shadow].context);
573
    }
574
 
575
    return 0;
576
}
577
 
578
/*----------------------------------------------------------------------*/
579
/* Generic signal. It increments the sem counter of n, and wakes all the*/
580
/* tasks that are blocked on the semaphore, if it is possible. The      */
581
/* semaphoric queue is a FIFO queue, in order to eliminate deadlocks    */
582
/*----------------------------------------------------------------------*/
583
int sem_xpost(sem_t *s, int n)
584
{
585
    struct sem_des *s1;        /* it speeds up access          */
586
    int p;                     /* idem                         */
587
    int fl = 0;                /* a flag                       */
588
    LEVEL l;
589
 
590
    if (*s < 0 || *s >= SEM_NSEMS_MAX || !sem_table[*s].used) {
591
      errno = EINVAL;
592
      return -1;
593
    }
594
 
595
    // ugly patch to call a sem_post!!!
596
    if (ll_ActiveInt()) {      
597
      SYS_FLAGS f;
598
      f = kern_fsave();
599
      s1 = &sem_table[*s];
600
      s1->count += n;                     /* inc sem count                */
601
 
602
      p = s1->blocked.first;
603
      while (p != NIL && sp_table[p].decsem <= s1->count) {
604
        /* Dec sem count */
605
        s1->count -= sp_table[p].decsem;
606
 
607
        /* Get task from blocked queue */
29 pj 608
        iq_extract(p,&s1->blocked);
2 pj 609
 
610
        l = proc_table[p].task_level;
38 pj 611
        level_table[l]->public_unblock(l,p);
2 pj 612
 
613
        /* Next task to wake            */
614
        p = s1->blocked.first;
615
 
616
        fl = 1;
617
      }
618
 
619
      /* tracer */
620
      trc_logevent(TRC_SEM_SIGNAL,s);
621
 
622
      /* Preempt if necessary */
623
      if (fl) event_need_reschedule();
624
      kern_frestore(f);
625
    }
626
    else {
627
      proc_table[exec].context = kern_context_save();
628
 
629
      s1 = &sem_table[*s];
630
      s1->count += n;                     /* inc sem count                */
631
 
632
      p = s1->blocked.first;
633
      while (p != NIL && sp_table[p].decsem <= s1->count) {
634
        /* Dec sem count */
635
        s1->count -= sp_table[p].decsem;
636
 
637
        /* Get task from blocked queue */
29 pj 638
        iq_extract(p,&s1->blocked);
2 pj 639
 
640
        l = proc_table[p].task_level;
38 pj 641
        level_table[l]->public_unblock(l,p);
2 pj 642
 
643
        /* Next task to wake            */
644
        p = s1->blocked.first;
645
 
646
        fl = 1;
647
      }
648
 
649
      /* tracer */
650
      trc_logevent(TRC_SEM_SIGNAL,s);
651
 
652
      /* Preempt if necessary */
653
      if (fl) scheduler();
654
 
655
      kern_context_load(proc_table[exec_shadow].context);
656
    }
657
 
658
    return 0;
659
}
660
 
661
/*----------------------------------------------------------------------*/
662
/* Getvalue returns the value of the semaphore (>=0). If someone is     */
663
/* blocked on the semaphore, return the number of process blocked (<0)  */
664
/*----------------------------------------------------------------------*/
665
int sem_getvalue(sem_t *sem, int *sval)
666
{
667
    PID p;
668
 
669
    if (*sem < 0 || *sem >= SEM_NSEMS_MAX || !sem_table[*sem].used) {
670
      errno = EINVAL;
671
      return -1;
672
    }
673
 
674
    kern_cli();
675
 
29 pj 676
    if (iq_isempty(&sem_table[*sem].blocked))
2 pj 677
      /* the sem is free */
678
      *sval = sem_table[*sem].count;
679
    else {
680
      /* the sem is busy */
681
      *sval = 0;
29 pj 682
      p = iq_query_first(&sem_table[*sem].blocked);
2 pj 683
      do {
684
        (*sval)--;
29 pj 685
        p = iq_query_next(p, &sem_table[*sem].blocked);
2 pj 686
      } while (p != NIL);
687
    }
688
 
689
    kern_sti();
690
    return 0;
691
}
692
 
693
 
694
/*----------------------------------------------------------------------*/
695
/* this function returns 1 if the task is blocked on a semaphore        */
696
/*----------------------------------------------------------------------*/
697
int isBlocked(PID i)
698
{
699
    if (proc_table[i].status == WAIT_SEM) return 1;
700
    else return 0;
701
}
702