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