]> rtime.felk.cvut.cz Git - frescor/fosa.git/blob - src_marte/fosa_threads_and_signals.c
Migrating FOSA trunk to d-ac2v2. Phase 1 moving FRSH-FOSA to FOSA
[frescor/fosa.git] / src_marte / fosa_threads_and_signals.c
1 //----------------------------------------------------------------------
2 //  Copyright (C) 2006 - 2007 by the FRESCOR consortium:
3 //
4 //    Universidad de Cantabria,              SPAIN
5 //    University of York,                    UK
6 //    Scuola Superiore Sant'Anna,            ITALY
7 //    Kaiserslautern University,             GERMANY
8 //    Univ. Politecnica  Valencia,           SPAIN
9 //    Czech Technical University in Prague,  CZECH REPUBLIC
10 //    ENEA                                   SWEDEN
11 //    Thales Communication S.A.              FRANCE
12 //    Visual Tools S.A.                      SPAIN
13 //    Rapita Systems Ltd                     UK
14 //    Evidence                               ITALY
15 //
16 //    See http://www.frescor.org
17 //
18 //        The FRESCOR project (FP6/2005/IST/5-034026) is funded
19 //        in part by the European Union Sixth Framework Programme
20 //        The European Union is not liable of any use that may be
21 //        made of this code.
22 //
23 //
24 //  based on previous work (FSF) done in the FIRST project
25 //
26 //   Copyright (C) 2005  Mälardalen University, SWEDEN
27 //                       Scuola Superiore S.Anna, ITALY
28 //                       Universidad de Cantabria, SPAIN
29 //                       University of York, UK
30 //
31 // This file is part of FOSA (Frsh Operating System Abstraction)
32 //
33 // FOSA is free software; you can redistribute it and/or modify it
34 // under terms of the GNU General Public License as published by the
35 // Free Software Foundation; either version 2, or (at your option) any
36 // later version.  FOSA is distributed in the hope that it will be
37 // useful, but WITHOUT ANY WARRANTY; without even the implied warranty
38 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 // General Public License for more details. You should have received a
40 // copy of the GNU General Public License along with FOSA; see file
41 // COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,
42 // Cambridge, MA 02139, USA.
43 //
44 // As a special exception, including FOSA header files in a file,
45 // instantiating FOSA generics or templates, or linking other files
46 // with FOSA objects to produce an executable application, does not
47 // by itself cause the resulting executable application to be covered
48 // by the GNU General Public License. This exception does not
49 // however invalidate any other reasons why the executable file might be
50 // covered by the GNU Public License.
51 // -----------------------------------------------------------------------
52 //fosa_threads_and_signals.c
53 //==============================================
54 //  ********  ******    ********  **********
55 //  **///// /**    **  **//////  /**     /**
56 //  **      /**    ** /**        /**     /**
57 //  ******* /**    ** /********* /**********
58 //  **////  /**    ** ////////** /**//////**
59 //  **      /**    **        /** /**     /**
60 //  **      /**    **  ********  /**     /**
61 //  //       /******/  ////////   //      // 
62 //
63 // FOSA(Frescor Operating System Adaptation layer)
64 //================================================
65
66 #include "fosa_threads_and_signals.h"
67 #include "fosa_configuration_parameters.h"
68
69 #include <pthread.h>
70 #include <stdio.h>
71 #include <sched.h>
72
73 #include <misc/error_checks.h>
74
75 /**
76  * @defgroup threadandsignals Thread and Signals
77  * @ingroup fosa
78  *
79  * This module defines the functions that manipulate frsh_threads and
80  * frsh_signals inside FRSH implementation.
81  *
82  * Applications can refer to FRSH threads but they cannot create them
83  * directly, instead they must use frsh_thread_create*() which in turn
84  * use fosa_thread_create().
85  *
86  * For signals, we assume that the OS provides a direct mapping
87  * for frsh_signal_t and frsh_signal_info_t in the native interface.
88  *
89  * @{
90  **/
91
92 //////////////////////////////////////////////////////////////////
93 // Storage of thread-specific keys
94 //   - key_list is an array containing the keys
95 //   - key_in_use is an array of booleans 
96 //   - key_lock is a mutex that locks the data structure 
97 //        while creating a new key
98 //   - keys_initialized indicates whether the key_in_use array and 
99 //        the key_lock mutex are initialized or not; it is declared
100 //        volatile as it may be accessed concurrently
101 //////////////////////////////////////////////////////////////////
102
103 static pthread_key_t key_list[FOSA_MAX_KEYS];
104 static bool key_in_use[FOSA_MAX_KEYS];
105 static pthread_once_t keys_initialized=PTHREAD_ONCE_INIT;
106 static pthread_mutex_t key_lock;
107
108
109 // Initialize the keys data structure
110 // This function should be called from pthread_once() to ensure
111 // one single initialization
112
113 void init_keys() {
114   pthread_mutexattr_t attr;
115   int i;
116
117   // initialize the key_in_use array and the mutex
118   for(i=0; i<FOSA_MAX_KEYS;i++) {
119     key_in_use[i]=false;
120   }
121   // Initialize the mutex
122   CHK(pthread_mutexattr_init(&attr));
123   // we use the priority inheritance protocol because we don't know which
124   // tasks will be using the mutex
125   CHK(pthread_mutexattr_setprotocol(&attr,PTHREAD_PRIO_INHERIT));
126   CHK(pthread_mutex_init(&key_lock,&attr));
127   CHK(pthread_mutexattr_destroy(&attr));
128 }
129
130 /*************************
131  * Thread identification
132  *************************/ 
133
134 /**
135  * fosa_thread_equal()
136  *
137  * Compare two thread identifiers to determine if they refer to the 
138  * same thread
139  **/
140 bool fosa_thread_equal(fosa_thread_id_t t1, fosa_thread_id_t t2)
141 {
142   return pthread_equal(t1,t2);
143 }
144
145
146 /**
147  * fosa_thread_self()
148  *
149  * Return the thread id of the calling thread
150  **/
151 fosa_thread_id_t fosa_thread_self()
152 {
153   return pthread_self();
154 }
155
156 /*************************
157  * Thread attributes
158  *************************/ 
159
160 /**
161  * fosa_thread_attr_init()
162  *
163  * Initialize a thread attributes object
164  *
165  * This function initializes the object pointed to by attr to all 
166  * the default values defined by FRSH
167  *
168  * @return 0 if successful; otherwise it returns \n
169  *   FOSA_ENOMEM: insufficient memory exists to initialize the thread 
170  *           attributes object
171  **/
172 int fosa_thread_attr_init(fosa_thread_attr_t *attr)
173 {
174   int ret_value;
175
176   ret_value=pthread_attr_init(attr);
177   if (ret_value==0) {
178     // set the default values
179
180     // detachstate = detached thread (no join operation allowed)
181     CHK(pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED));
182
183     // inheritsched = explicit, so that we can explicitly set the attributes
184     CHK(pthread_attr_setinheritsched(attr,PTHREAD_EXPLICIT_SCHED));
185
186     // schedpolicy = fixed priorities
187     CHK(pthread_attr_setschedpolicy(attr,SCHED_FIFO));
188
189     // detachstate = detached thread (no join operation allowed)
190     CHK(pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED));
191
192   }
193   return ret_value;
194 }
195
196 /**
197  * fosa_thread_attr_destroy()
198  *
199  * Destroy a thread attributes object
200  *
201  * This function is used to destroy the thread attributes object,
202  * pointed to by attr, and deallocate any system resources allocated for it
203  * 
204  * Returns 0
205  */
206 int fosa_thread_attr_destroy(fosa_thread_attr_t *attr)
207 {
208   return pthread_attr_destroy(attr);
209 }
210
211 /**
212  * fosa_thread_attr_set_stacksize()
213  *
214  * Set the thread minimum stack size in a thread attributes object
215  *
216  * This function sets the minimum stack size of the thread attributes
217  * object attr to the value given by stacksize, in bytes. This
218  * function has no runtime effect on the stack size, except when the
219  * attributes object is used to create a thread, when it will be
220  * created with the specified minimum stack size
221  * 
222  * @return 0 if successful, or the following error code:
223  *    FOSA_EINVAL: the specified stacksize  value is not supported in
224  *            this implementation
225  */
226 int fosa_thread_attr_set_stacksize
227      (fosa_thread_attr_t *attr, size_t stacksize)
228 {
229   return pthread_attr_setstacksize(attr,stacksize);
230 }
231
232 /**
233  * fosa_thread_attr_get_stacksize()
234  *
235  * Get the thread minimum stack size from a thread attributes object
236  *
237  * This function sets the variable pointed to by stacksize to the
238  * minimum stack size stored in the thread attributes object attr.
239  * 
240  * @return 0
241  */
242 int fosa_thread_attr_get_stacksize
243       (const fosa_thread_attr_t *attr, size_t *stacksize)
244 {
245   return pthread_attr_getstacksize(attr,stacksize);
246 }
247
248
249 /*************************
250  * Thread creation and termination
251  *************************/ 
252
253 /**
254  * fosa_thread_create()
255  *
256  * This function creates a new thread using the attributes specified
257  * in attr. If attr is NULL, default attributes are used. The new
258  * thread starts running immediately, executing the function specified
259  * by code, with an argument equal to arg. Upon successful return, the
260  * variable pointed to by tid will contain the identifier of the newly
261  * created thread. The set of signals that may be synchronously
262  * accepted is inherited from the parent thread.
263  *
264  * Returns 0 if successful; otherwise it returs a code error:
265  *
266  *     EAGAIN: the system lacks the necessary resources to create a
267  *             new thread or the maximum number of threads has been
268  *             reached
269  *
270  *     EINVAL: the value specified by attr is invalid (for instance,
271  *              it has not been correctly initialized)
272  *
273  *     EREJECT: the cretion of the thread was rejected by the frsh scheduler
274  *               possibly because of incorrect attributes, or because the 
275  *               requested minimum capacity cannot be guaranteed
276  *
277  **/
278  int fosa_thread_create
279     (fosa_thread_id_t *tid, const fosa_thread_attr_t *attr, 
280      fosa_thread_code_t code, void * arg)
281 {
282   return pthread_create(tid,attr,code,arg);
283 }
284
285
286 /**
287  * Note: no thread termination primitive is provided. The termination
288  * of a thread will be notified by the system to the FRSH scheduler
289  * through the scheduler API
290  **/
291
292
293 /**************************************************
294  * Thread-specific data
295  *  (extended with access from a different thread)
296  *
297  * Several data items (pointers) may be associated with each thread
298  * Each item is identified through a key, an integer value between 0
299  * and FOSA_MAX_KEYS-1. The caller is responsible of allocating and
300  * deallocating the memory area pointed to by the pointer
301  **************************************************/ 
302
303 /**
304  * fosa_key_create()
305  *
306  * Create a new key for thread specific data.
307  *
308  * Prior to setting data in a key, we need ask the system to create
309  * one for us.
310  *
311  * @return 0 if successful \n
312  *   FOSA_EINVAL If we already have reached the FOSA_MAX_KEYS limit.
313  *   FOSA_ENOMEM If there are no enough memory resources to 
314  *               create the key.
315  **/
316 int fosa_key_create(int *key)
317 {
318   int i,ret_value;
319   bool found=false;
320
321   ret_value = 0;
322   // initialize the keys data structure if needed
323   CHK(pthread_once(&keys_initialized, init_keys));
324
325   // lock the mutex
326   CHK(pthread_mutex_lock(&key_lock));
327
328   // find an unused key
329   for(i=0; i<FOSA_MAX_KEYS;i++) {
330     if (!key_in_use[i]) {
331       *key=i;
332       key_in_use[i]=true;
333       ret_value=pthread_key_create(&(key_list[i]),NULL);
334       found=true;
335       break;
336     }
337   }
338   // unlock the mutex before returning
339   CHK(pthread_mutex_unlock(&key_lock));
340   if (!found) {
341     // all keys are in use; max keys reached
342     ret_value=FOSA_EINVAL;
343   }
344   return ret_value;
345 }
346
347 /**
348  * fosa_key_destroy()
349  *
350  * Destroy a key
351  *
352  * This destroys the key and isables its use in the system
353  *
354  * @return 0 if successful \n
355  *   FOSA_EINVAL The key is not initialised or is not in FOSA key range.
356  **/
357 int fosa_key_destroy(int key)
358 {
359   int ret_value;
360
361   // lock the mutex
362   CHK(pthread_mutex_lock(&key_lock));
363
364   // destroy the key and mark it as available
365   ret_value=pthread_key_delete(key_list[key]);
366   if (ret_value==0) {
367     key_in_use[key]=false;
368   }
369
370   // unlock the mutex before returning
371   CHK(pthread_mutex_unlock(&key_lock));
372   return ret_value;
373 }
374
375
376 /**
377  * fosa_thread_set_specific_data()
378  *
379  * Set thread-specific data
380  *
381  * For the thread identified by tid, the thread-specifid data field
382  * identified by key will be set to the value specified by value
383  *
384  * Returns 0 if successful; otherwise, an error code is returned
385  *     EINVAL: the value of key is not between 0 and FOSA_MAX_KEYS-1
386  *
387  * Alternatively, in case of error the implementation is allowed to
388  * notify it to the system console and then terminate the FRSH
389  * implementation and dependant applications
390  **/
391  int fosa_thread_set_specific_data
392        (int key, fosa_thread_id_t tid, const void * value)
393 {
394   return pthread_setspecific_for(key_list[key],tid,value);
395 }
396
397 /**
398  * fosa_thread_get_specific_data()
399  *
400  * Get thread-specific data
401  *
402  * For the thread identified by tid, the thread-specifid data field
403  * identified by key will be copied to the variable pointed to by value
404  *
405  * Returns 0 if successful; otherwise, an error code is returned
406  *     EINVAL: the value of key is not between 0 and FOSA_MAX_KEYS-1
407  *
408  * Alternatively, in case of error the implementation is allowed to
409  * notify it to the system console and then terminate the FRSH
410  * implementation and dependant applications
411  **/
412 int fosa_thread_get_specific_data(int key, fosa_thread_id_t tid, 
413                                   void ** value)
414 {
415   return pthread_getspecific_from(key_list[key],tid,value);
416 }
417
418
419 /******************************************************************
420  * Thread scheduling
421  * 
422  * This implementation of FRSH assumes an underlying fixed priority
423  * scheduler with priorities in a range, with a minimum and a
424  * maximumm, a number of priority levels with at least 31
425  * priorities. A larger number implies a larger priority. In systems
426  * in which the underlying scheduler uses the opposite convention, a
427  * mapping is automatically provided by the OS adaptation layer.
428  *******************************************************************/
429
430 /**
431  * fosa_get_priority_max()
432  *
433  * Return the maximum priority value used in this implementation
434  **/
435 int fosa_get_priority_max()
436 {
437   return sched_get_priority_max(SCHED_APP);
438 }
439
440 /**
441  * fosa_get_priority_min()
442  *
443  * Return the minimum priority value used in this implementation
444  **/
445 int fosa_get_priority_min()
446 {
447   return sched_get_priority_min(SCHED_APP);
448 }
449
450 /**
451  * fosa_thread_attr_set_prio()
452  *
453  * Change the priority of a thread attributes object
454  *
455  * The priority of the thread attriutes object specified by attr is
456  * set to the value specified by prio. This function has no runtime
457  * effect on the priority, except when the attributes object is used
458  * to create a thread, when it will be created with the specified
459  * priority
460  * 
461  * Returns 0 if successful, or the following error code:
462  *    EINVAL: the specified priority value is not between the 
463  *            minimum and the maximum priorities defined in this
464  *            FRSH implementation
465  * Alternatively, in case of error the implementation is allowed to
466  * notify it to the system console and then terminate the FRSH
467  * implementation and dependant applications
468  **/
469 int fosa_thread_attr_set_prio(fosa_thread_attr_t *attr, int prio)
470 {
471   struct sched_param param;
472
473   param.sched_priority=prio;
474   return pthread_attr_setschedparam(attr,&param);
475 }
476
477 /**
478  * fosa_thread_attr_get_prio()
479  *
480  * Get the priority from a thread attributes object
481  *
482  * This function sets the variable pointed to by prio to the
483  * priority stored in the thread attributes object attr.
484  * 
485  * Returns 0
486  **/
487 int fosa_thread_attr_get_prio
488           (const fosa_thread_attr_t *attr, int *prio)
489 {
490   struct sched_param param;
491   int ret_value;
492   
493   ret_value=pthread_attr_getschedparam(attr,&param);
494   if (ret_value==0) {
495     *prio=param.sched_priority;
496   }
497   return ret_value;
498 }
499
500 /**
501  * fosa_thread_set_prio()
502  *
503  * Dynamically change the priority of a thread
504  *
505  * The priority of the thread identified by tid is
506  * set to the value specified by prio. 
507  * 
508  * Returns 0 if successful, or the following error code:
509  *    EINVAL: the specified priority value is not between the 
510  *            minimum and the maximum priorities defined in this
511  *            FRSH implementation
512  * Alternatively, in case of error the implementation is allowed to
513  * notify it to the system console and then terminate the FRSH
514  * implementation and dependant applications
515  **/
516 int fosa_thread_set_prio(fosa_thread_id_t tid, int prio)
517 {
518   struct sched_param param;
519   int policy, ret_value;
520
521   ret_value=pthread_getschedparam(tid,&policy,&param);
522   if (ret_value!=0) return ret_value;
523
524   param.sched_priority=prio;
525   return pthread_setschedparam(tid,policy,&param);
526 }
527
528 /**
529  * fosa_thread_get_prio()
530  *
531  * Dynamically get the priority of a thread
532  *
533  * This function sets the variable pointed to by prio to the
534  * priority of the thread identified by tid
535  * 
536  * Returns 0
537  **/
538 int fosa_thread_get_prio (fosa_thread_id_t tid, int *prio)
539 {
540   struct sched_param param;
541   int policy, ret_value;
542
543   ret_value=pthread_getschedparam(tid,&policy,&param);
544   if (ret_value==0) {
545     *prio=param.sched_priority;
546   }
547   return ret_value;
548 }
549
550
551
552 /*******************************************************************
553  * Signals
554  *
555  * Signals represent events that may be notified by the system, or
556  * sent explicitly by the application, and for which a thread may
557  * synchronously wait. Signals carry an associated piece of
558  * information (an integer or a pointer) and are queued until they are
559  * accepted.  Signals are identified by an integer signal number (of
560  * the type fosa_signal_t) in the range FOSA_SIGNAL_MIN,
561  * FOSA_SIGNAL_MAX.  This range is required to have at least <tbd>
562  * values.
563  *******************************************************************/
564
565 /**
566  * fosa_set_accepted_signals()
567  *
568  * Establish the set of signals that may be synchronously accepted 
569  * by the calling thread
570  *
571  * The function uses the array of signal numbers specified by set,
572  * which must be of size equal to size
573  *
574  * Returns 0 if successful; otherwise it returns an error code:
575  *     EINVAL: the array contains one or more values which are not
576  *             between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX, or size
577  *             is less than 0
578  *
579  * Alternatively, in case of error the implementation is allowed to
580  * notify it to the system console and then terminate the FRSH
581  * implementation and dependant applications
582  **/
583 int fosa_set_accepted_signals(fosa_signal_t set[], int size)
584 {
585   sigset_t signalset;
586   int i;
587   struct sigaction act;
588
589   // empty the signal set
590   CHKE(sigemptyset(&signalset));
591
592   // configure the signal action to make the signal a real-time
593   //   signal that can be queued
594   act.sa_handler=SIG_DFL;
595   act.sa_mask=signalset;
596   act.sa_flags=SA_SIGINFO;
597   act.sa_sigaction=NULL;
598
599     // loop for all signals in set
600   for(i=0;i<size;i++) {
601     CHKE(sigaddset(&signalset,set[i]));
602     // Configure the signal so that it can be queued with data
603     CHKE(sigaction(set[i],&act,NULL));
604   } 
605   return pthread_sigmask(SIG_BLOCK,&signalset,NULL);
606 }
607
608 /**
609  * fosa_signal_queue()
610  *
611  * Queue a signal
612  *
613  * This function is used to explicitly send a signal with a specified
614  * value
615  * 
616  * The signal number specified by signal is sent together with the
617  * information specified by info, to the thread identified by
618  * receiver. In those implementations that do not support queueing a
619  * signal with information to a thread (such as POSIX), the signal may
620  * be sent to any thread that is waiting for this signal via
621  * fosa_signal_wait(). Portability can be ensured by having the receiver
622  * thread be the one who is waiting for the signal. 
623  *
624  * Returns 0 if successful; otherwise it returns an error code:
625  *     EINVAL: the signal specified by signal is not
626  *              between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX
627  *
628  *     EAGAIN: no resources are available to queue the signal; the
629  *             maximum number of queued signals has been reached, or a
630  *             systemwide resource limit has been exceeded
631  *
632  * Alternatively, in case of error the implementation is allowed to
633  * notify it to the system console and then terminate the FRSH
634  * implementation and dependant applications
635  **/
636 int fosa_signal_queue
637        (fosa_signal_t signal, fosa_signal_info_t info,
638         fosa_thread_id_t receiver)
639 {
640   // note: in MaRTE OS the signal is sent to any interested thread
641   pid_t pid=1; // dummy value; the pid is ignored in MaRTE OS
642   int err;
643   err=sigqueue(pid,signal,*((union sigval *)(&info)));
644   // the above casting construct is used to overcome the compiler
645   // restriction that does not allow casts between unions
646   if (err==0) {
647     return 0;
648   } else {
649     return errno;
650   }
651 }
652
653
654 /**
655  * fosa_signal_wait()
656  *
657  * Wait for a signal
658  * 
659  * The function waits for the arrival of one of the signals in the
660  * array of signal numbers specified by set, which must be of size
661  * equal to size. If there is a signal already queued, the function
662  * returns immediately. If there is no signal of the specified set
663  * queued, the calling thread is suspended until a signal from that
664  * set arrives. Upon return, if signal_received is not NULL the number
665  * of the signal received is stored in the variable pointed to by
666  * signal_received; and if info is not NULL the associated information
667  * is stored in the variable pointed to by info.
668  *
669  * Returns 0 if successful; otherwise it returns an error code:
670  *     EINVAL: the array contains one or more values which are not
671  *             between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX, or size
672  *             is less than 0
673  *
674  * Alternatively, in case of error the implementation is allowed to
675  * notify it to the system console and then terminate the FRSH
676  * implementation and dependant applications
677  **/
678 int fosa_signal_wait
679       (fosa_signal_t set[], int size, fosa_signal_t *signal_received, 
680        fosa_signal_info_t *info)
681 {
682   int err,i;
683   sigset_t signalset;
684   siginfo_t siginfo;
685
686   CHKE(sigemptyset(&signalset));
687   for(i=0;i<size;i++) {
688     CHKE(sigaddset(&signalset,set[i]));
689   } 
690
691   err=sigwaitinfo(&signalset,&siginfo);
692   if (err!=-1) {
693     *signal_received=siginfo.si_signo;
694     *info=*((fosa_signal_info_t *)(&siginfo.si_value));
695     return 0;
696   } else {
697     return errno;
698   }
699 }
700
701 /**
702  * fosa_signal_timedwait()
703  *
704  * Timed wait for a signal
705  * 
706  * This function behaves the same as fosa_signal_wait(), except that
707  * the suspension time is limited to the time interval specified in
708  * the timespec structure referenced by timeout.
709  * 
710  * Returns 0 if successful; otherwise it returns an error code:
711  *     EINVAL: the array contains one or more values which are not
712  *             between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX, or size
713  *             is less than 0, or timeout is invalid
714  *     EAGAIN: The timeout expired
715  *
716  * Alternatively, in case of the EINVAL error the implementation is
717  * allowed to notify it to the system console and then terminate the
718  * FRSH implementation and dependant applications
719  **/
720  int fosa_signal_timedwait
721       (fosa_signal_t set[], int size, fosa_signal_t *signal_received, 
722        fosa_signal_info_t *info, const struct timespec *timeout)
723 {
724
725   // the implementation of sigtimedwait is not yet available in MaRTE OS
726   return ENOSYS;
727
728 /*   int err,i; */
729 /*   sigset_t signalset; */
730 /*   siginfo_t siginfo; */
731
732 /*   CHKE(sigemptyset(&signalset)); */
733 /*   for(i=0;i<size;i++) { */
734 /*     CHKE(sigaddset(&signalset,set[i])); */
735 /*   }  */
736
737 /*   err=sigtimedwait(&signalset,&siginfo,timeout); */
738 /*   if (err==0) { */
739 /*     *signal_received=siginfo.si_signo; */
740 /*     *info=siginfo.si_value; */
741 /*     return 0; */
742 /*   } else { */
743 /*     return errno; */
744 /*   } */
745 }
746