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