]> rtime.felk.cvut.cz Git - frescor/fosa.git/blobdiff - src_aquosa/fosa_threads_and_signals.c
Updated for compilation on older systems without PTHREAD_PRIO_INHERIT
[frescor/fosa.git] / src_aquosa / fosa_threads_and_signals.c
index d83b7a21e58e2fcd8d3413a9b86140468403181d..a5910719576f8858a49e764acc8c30422cdb8035 100644 (file)
 // FOSA(Frescor Operating System Adaptation layer)
 //================================================
 
-#include <linux/unistd.h>
-#include <unistd.h>
-#include <fosa.h>
+#include "fosa_time.h"
+#include "fosa_configuration_parameters.h"
+#include "fosa_threads_and_signals.h"
+
+#ifdef OMK_FOR_USER            /* If compiled by OMK, use the config */
+#include "fosa_config.h"
+#endif
+
+/*************************
+ * Storage of thread-specific keys
+ *************************/
+
+static pthread_key_t key_list[FOSA_MAX_KEYS];
+static bool key_in_use[FOSA_MAX_KEYS];
+static pthread_mutex_t key_lock;
+
+
+/* Initialize the keys data structure */
+int init_keys()
+{
+       int i, ret;
+       pthread_mutexattr_t attr;
+
+       for(i = 0; i < FOSA_MAX_KEYS; i++)
+               key_in_use[i] = false;
+
+       ret = pthread_mutexattr_init(&attr);
+       if (ret) return errno;
+
+#ifndef CONFIG_NO_PRIO_INHERIT /* Not supported on older systems */
+       ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
+       if (ret) return errno;
+#endif
+
+       ret = pthread_mutex_init(&key_lock,&attr);
+       if (ret) return errno;
+
+       return 0;
+}
 
 /*************************
  * Thread identification
  *************************/ 
 
+/**
+ * fosa_thread_equal()
+ *
+ * Compare two thread identifiers to determine if they refer to the 
+ * same thread
+ **/
 bool fosa_thread_equal(fosa_thread_id_t t1, fosa_thread_id_t t2)
 {
-       if ( pthread_equal(t1.pthread_id, t2.pthread_id &&
-                       t1.linux_pid == t2.linux_pid    &&
-                       t1.linux_tid == t2.linux_tid) )
-               return true;
-
-       return false;
+       return t1.linux_pid == t2.linux_pid &&
+              t1.linux_tid == t2.linux_tid &&
+              pthread_equal(t1.pthread_id, t2.pthread_id);
 }
 
+/**
+ * fosa_thread_self()
+ *
+ * Return the thread id of the calling thread
+ **/
 fosa_thread_id_t fosa_thread_self()
 {
        fosa_thread_id_t thread_self;
-       /* 
-        * Fill the user pointer with appropriate data for the calling thread
+       /** 
         * NB. Remember:
         *  fosa_thread_id_t => struct {
         *                       pthread_t pthread_id;
         *                       pid_t linux_pid;
         *                       tid_t linux_tid;
         * };
-        */
-       thread_self.pthread_id = pthread_self();        /* only valid for threads! */
+        **/
+       thread_self.pthread_id = pthread_self();
        thread_self.linux_pid = getpid();
-       thread_self.linux_tid = syscall(__NR_gettid);   /* equal to gettid() */
+       thread_self.linux_tid = syscall(__NR_gettid);   /* gettid() */
 
        return thread_self;
 }
 
+/*************************
+ * Thread attributes
+ *************************/
+
+static inline int __fosa_check_thread(const fosa_thread_id_t *tid)
+{
+       if (tid->linux_pid == tid->linux_tid)
+               return 0;
+
+       return 1;
+}
+
+/**
+ * fosa_thread_attr_init()
+ *
+ * Initialize a thread attributes object
+ *
+ * This function initializes the object pointed to by attr to all 
+ * the default values defined by FRSH
+ *
+ * return 0 if successful; otherwise it returns
+ *   FOSA_ENOMEM: insufficient memory exists to initialize the thread 
+ *           attributes object
+ **/
+int fosa_thread_attr_init(fosa_thread_attr_t *attr)
+{
+       return pthread_attr_init(attr);
+}
+
+/**
+ * fosa_thread_attr_destroy()
+ *
+ * Destroy a thread attributes object
+ *
+ * This function is used to destroy the thread attributes object,
+ * pointed to by attr, and deallocate any system resources allocated for it
+ * 
+ * Returns 0
+ **/
+int fosa_thread_attr_destroy(fosa_thread_attr_t *attr)
+{
+       return pthread_attr_destroy(attr);
+}
+
+/**
+ * fosa_thread_attr_set_stacksize()
+ *
+ * Set the thread minimum stack size in a thread attributes object
+ *
+ * This function sets the minimum stack size of the thread attributes
+ * object attr to the value given by stacksize, in bytes. This
+ * function has no runtime effect on the stack size, except when the
+ * attributes object is used to create a thread, when it will be
+ * created with the specified minimum stack size
+ * 
+ * return 0 if successful, or the following error code:
+ *    FOSA_EINVAL: the specified stacksize  value is not supported in
+ *            this implementation
+ **/
+int fosa_thread_attr_set_stacksize(fosa_thread_attr_t *attr,
+                                  size_t stacksize)
+{
+       return pthread_attr_setstacksize(attr, stacksize);
+}
+
+/**
+ * fosa_thread_attr_get_stacksize()
+ *
+ * Get the thread minimum stack size from a thread attributes object
+ *
+ * This function sets the variable pointed to by stacksize to the
+ * minimum stack size stored in the thread attributes object attr.
+ * 
+ * return 0
+ **/
+int fosa_thread_attr_get_stacksize(const fosa_thread_attr_t *attr,
+                                  size_t *stacksize)
+{
+       return pthread_attr_getstacksize(attr, stacksize);
+}
+
 /*************************
  * Thread creation and termination
- *************************/ 
+ *************************/
 
+/**
+ * fosa_thread_create()
+ *
+ * This function creates a new thread using the attributes specified
+ * in attr. If attr is NULL, default attributes are used. The new
+ * thread starts running immediately, executing the function specified
+ * by code, with an argument equal to arg. Upon successful return, the
+ * variable pointed to by tid will contain the identifier of the newly
+ * created thread. The set of signals that may be synchronously
+ * accepted is inherited from the parent thread.
+ *
+ * Returns 0 if successful; otherwise it returs a code error:
+ *
+ *     EAGAIN: the system lacks the necessary resources to create a
+ *             new thread or the maximum number of threads has been
+ *             reached
+ *
+ *     EINVAL: the value specified by attr is invalid (for instance,
+ *              it has not been correctly initialized)
+ *
+ *     EREJECT: the cretion of the thread was rejected by the frsh scheduler
+ *               possibly because of incorrect attributes, or because the 
+ *               requested minimum capacity cannot be guaranteed
+ **/
 int fosa_thread_create(fosa_thread_id_t *tid,
-       const fosa_thread_attr_t *attr,
-       fosa_thread_code_t code,
-       void *arg)
+                      const fosa_thread_attr_t *attr,
+                      fosa_thread_code_t code,
+                      void *arg)
 {
-       pthread_t tmp_tid;
-
-       /* 'tid' can't be used, act other ways
-        * to get the thread id of the new thread */
-       return pthread_create(&tmp_tid,attr,code, arg);
+       return pthread_create(&tid->pthread_id, attr, code, arg);
 }
 
+/**
+ * Note: no thread termination primitive is provided. The termination
+ * of a thread will be notified by the system to the FRSH scheduler
+ * through the scheduler API
+ **/
+
 /**************************************************
  * Thread-specific data
  *  (extended with access from a different thread)
@@ -120,34 +270,130 @@ int fosa_thread_create(fosa_thread_id_t *tid,
  * deallocating the memory area pointed to by the pointer
  **************************************************/ 
 
-int fosa_thread_set_specific_data(int key,
-       fosa_thread_id_t tid,
-       const void *value)
+/**
+ * fosa_key_create()
+ *
+ * Create a new key for thread specific data.
+ *
+ * Prior to setting data in a key, we need ask the system to create
+ * one for us.
+ *
+ * return 0 if successful \n
+ *   FOSA_EINVAL If we already have reached the FOSA_MAX_KEYS limit.
+ *   FOSA_ENOMEM If there are no enough memory resources to 
+ *               create the key.
+ **/
+int fosa_key_create(int *key)
+{
+       int i, ret;
+       bool found = false;
+
+       ret = pthread_mutex_lock(&key_lock);
+       if (ret) return ret;
+
+       /* find an unused key */
+       for (i = 0; i < FOSA_MAX_KEYS; i++) {
+               if (!key_in_use[i]) {
+                       ret = pthread_key_create(&(key_list[i]), NULL);
+                       if (ret) return ret;
+
+                       *key = i;
+                       key_in_use[i] = true;
+                       found = true;
+
+                       break;
+               }
+       }
+
+       ret = pthread_mutex_unlock(&key_lock);
+       if (ret) return ret;
+
+       return (!found ? FOSA_EINVAL : ret);
+}
+
+/**
+ * fosa_key_destroy()
+ *
+ * Destroy a key
+ *
+ * This destroys the key and isables its use in the system
+ *
+ * return 0 if successful
+ *   FOSA_EINVAL The key is not initialised or is not in FOSA key range.
+ **/
+int fosa_key_destroy(int key)
+{
+       int ret;
+
+       ret = pthread_mutex_lock(&key_lock);
+       if (ret) return ret;
+
+       ret = pthread_key_delete(key_list[key]);
+       if (ret) return ret;
+
+       key_in_use[key]=false;
+
+       ret = pthread_mutex_unlock(&key_lock);
+       if (ret) return ret;
+
+       return 0;
+}
+
+
+/**
+ * fosa_thread_set_specific_data()
+ *
+ * Set thread-specific data
+ *
+ * For the thread identified by tid, the thread-specifid data field
+ * identified by key will be set to the value specified by value
+ * 
+ * In this implementation, according to POSIX, the accessed data field
+ * is the one of the calling thread, not the one specified via tid.
+ *
+ * Returns 0 if successful; otherwise, an error code is returned
+ *     EINVAL: the value of key is not between 0 and FOSA_MAX_KEYS-1
+ **/
+ int fosa_thread_set_specific_data(int key,
+                                  fosa_thread_id_t tid,
+                                  const void * value)
 {
-       //if ((key > 0) && (key < (FOSA_MAX_KEYS-1))) {
-       //      tid.pthread_id->tsd[key] = (void *) value;
-       //
-       //      return 0;
-       //}
-
-       errno = EINVAL;
-       return -1;
+       int ret;
+
+       /* only POSIX threads can have specific data */
+       if (!__fosa_check_thread(&tid))
+               return FOSA_EINVAL;
+
+       ret = pthread_setspecific(key_list[key], value);
+
+       return ret ? ret : 0;
 }
 
+/**
+ * fosa_thread_get_specific_data()
+ *
+ * Get thread-specific data
+ *
+ * For the thread identified by tid, the thread-specifid data field
+ * identified by key will be copied to the variable pointed to by value
+ * 
+ * In this implementation, according to POSIX, the accessed data field
+ * is the one of the calling thread, not the one specified via tid.
+ *
+ * Returns 0 if successful; otherwise, an error code is returned
+ *     EINVAL: the value of key is not between 0 and FOSA_MAX_KEYS-1
+ **/
 int fosa_thread_get_specific_data(int key,
-       fosa_thread_id_t tid,
-       void **value)
+                                 fosa_thread_id_t tid,
+                                 void ** value)
 {
-       //if ((key > 0) && (key < (FOSA_MAX_KEYS-1))) {
-       //      *value=pthread_remote_getspecific(key,tid);
-       //      *value=tid.pthread_id->tsd[key];
-       //
-       //      return 0;
-       //}
+       /* only POSIX threads can have specific data */
+       if (!__fosa_check_thread(&tid))
+               return EINVAL;
 
-       errno = EINVAL;
-       return -1;
+       value = pthread_getspecific(key_list[key]);
 
+       return !value ? FOSA_EINVAL : 0;
 }
 
 /******************************************************************
@@ -161,53 +407,129 @@ int fosa_thread_get_specific_data(int key,
  * mapping is automatically provided by the OS adaptation layer.
  *******************************************************************/
 
-int fosa_get_priority_max() {
-       return sched_get_priority_max(0);
+/**
+ * fosa_get_priority_max()
+ *
+ * Return the maximum priority value used in this implementation
+ **/
+int fosa_get_priority_max()
+{
+       int ret;
+
+       ret = sched_get_priority_max(SCHED_RR);
+
+       return ret ? errno : 0;
 }
 
-int fosa_get_priority_min() {
-       return sched_get_priority_min(0);
+/**
+ * fosa_get_priority_min()
+ *
+ * Return the minimum priority value used in this implementation
+ **/
+int fosa_get_priority_min()
+{
+       int ret;
+
+       ret = sched_get_priority_min(SCHED_RR);
+
+       return ret ? errno : 0;
 }
 
+/**
+ * fosa_thread_attr_set_prio()
+ *
+ * Change the priority of a thread attributes object
+ *
+ * The priority of the thread attriutes object specified by attr is
+ * set to the value specified by prio. This function has no runtime
+ * effect on the priority, except when the attributes object is used
+ * to create a thread, when it will be created with the specified
+ * priority
+ * 
+ * Returns 0 if successful, or the following error code:
+ *    EINVAL: the specified priority value is not between the 
+ *            minimum and the maximum priorities defined in this
+ *            FRSH implementation
+ **/
 int fosa_thread_attr_set_prio(fosa_thread_attr_t *attr, int prio)
 {
-       //if ((sched_get_priority_min(0)<=prio) || (prio<=sched_get_priority_min(0))) {
-       //      attr->sched_param.sched_priority = prio;
-       //
-       //      return 0;
-       //}
-
-       errno = EINVAL;
-       return -1;
+       int ret;
+       struct sched_param param;
+
+       param.sched_priority = prio;
+       ret = pthread_attr_setschedpolicy(attr, SCHED_RR);
+       if (ret) return ret;
+
+       return pthread_attr_setschedparam(attr, &param);
 }
 
-int fosa_thread_attr_get_prio (const fosa_thread_attr_t *attr, int *prio)
+/**
+ * fosa_thread_attr_get_prio()
+ *
+ * Get the priority from a thread attributes object
+ *
+ * This function sets the variable pointed to by prio to the
+ * priority stored in the thread attributes object attr.
+ * 
+ * Returns 0
+ **/
+int fosa_thread_attr_get_prio(const fosa_thread_attr_t *attr, int *prio)
 {
-       //*prio = attr->sched_param.sched_priority;
-       //return 0;
+       int ret;
+       struct sched_param param;
+
+       ret = pthread_attr_getschedparam(attr, &param);
+       if (ret) return ret;
 
-       errno = EINVAL;
-       return -1;
+       *prio = param.sched_priority;
+
+       return 0;
 }
 
+/**
+ * fosa_thread_set_prio()
+ *
+ * Dynamically change the priority of a thread
+ *
+ * The priority of the thread identified by tid is
+ * set to the value specified by prio. 
+ * 
+ * Returns 0 if successful, or the following error code:
+ *    EINVAL: the specified priority value is not between the 
+ *            minimum and the maximum priorities defined in this
+ *            FRSH implementation
+ **/
 int fosa_thread_set_prio(fosa_thread_id_t tid, int prio)
 {
-       //if ((sched_get_priority_min(0)<=prio) || (prio<=sched_get_priority_min(0))) {
-       //      pthread_setschedprio(tid.pthread_id,prio);
-       //      return 0;
-       //}
+       int ret;
+       struct sched_param param;
+
+       param.sched_priority = prio;
 
-       errno = EINVAL;
-       return -1;
+       ret = sched_setscheduler(0, SCHED_RR, &param);
+
+       return ret ? errno : 0;
 }
 
-int fosa_thread_get_prio (fosa_thread_id_t tid, int *prio)
+/**
+ * fosa_thread_get_prio()
+ *
+ * Dynamically get the priority of a thread
+ *
+ * This function sets the variable pointed to by prio to the
+ * priority of the thread identified by tid
+ * 
+ * Returns 0
+ **/
+int fosa_thread_get_prio(fosa_thread_id_t tid, int *prio)
 {
-       //*prio = tid.pthread_id->sched_param.sched_priority;
-       //return 0;
+       struct sched_param param;
+       int ret;
+
+       ret = sched_getparam(0, &param);
+       *prio = param.sched_priority;
 
-       errno = EINVAL;
-       return -1;
+       return ret ? errno : 0;
 }
 
 /*******************************************************************
@@ -223,86 +545,225 @@ int fosa_thread_get_prio (fosa_thread_id_t tid, int *prio)
  * values.
  *******************************************************************/
 
-/* it's an hack and it does not work!
- * We need to change the API if we want such a feature!!*/
-sigset_t original_mask;
-
+/**
+ * fosa_set_accepted_signals()
+ *
+ * Establish the set of signals that may be synchronously accepted 
+ * by the calling thread
+ *
+ * The function uses the array of signal numbers specified by set,
+ * which must be of size equal to size
+ *
+ * Returns 0 if successful; otherwise it returns an error code:
+ *     EINVAL: the array contains one or more values which are not
+ *             between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX, or size
+ *             is less than 0
+ *
+ * Alternatively, in case of error the implementation is allowed to
+ * notify it to the system console and then terminate the FRSH
+ * implementation and dependant applications
+ **/
 int fosa_set_accepted_signals(fosa_signal_t set[], int size)
 {
-       int x;
-       sigset_t new_mask;
-
-       /* all signal blocked by default */
-       sigfillset(&new_mask);
-       for (x = 0; x < size; x++)
-               /* unblock only the signals in 'set' */
-               sigdelset(&new_mask, set[x]);
-
-       /* NB. save the original mask in the
-        * default variable... Orrible hack!! :-( */
-       return pthread_sigmask(SIG_SETMASK, &new_mask, &original_mask);
+       int i, ret;
+       fosa_thread_id_t self;
+       sigset_t sigset;
+       struct sigaction action;
+
+       ret = sigemptyset(&sigset);
+       if (ret) goto err;
+
+       action.sa_handler = SIG_DFL;
+       action.sa_mask = sigset;
+       action.sa_flags = SA_SIGINFO;
+       action.sa_sigaction = NULL;
+
+       for (i = 0; i < size; i++) {
+               ret = sigaddset(&sigset, set[i]);
+               if (ret) goto err;
+               ret = sigaction(set[i], &action, NULL);
+               if (ret) goto err;
+       }
+
+       self = fosa_thread_self();
+       if (__fosa_check_thread(&self)) {
+               ret = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
+               if (ret) return ret;
+       } else {
+               ret = sigprocmask(SIG_BLOCK, &sigset, NULL);
+               if (ret) goto err;
+       }
+
+       return 0;
+err:
+       return errno;
 }
 
+/**
+ * fosa_signal_queue()
+ *
+ * Queue a signal
+ *
+ * This function is used to explicitly send a signal with a specified
+ * value
+ * 
+ * The signal number specified by signal is sent together with the
+ * information specified by info, to the thread identified by
+ * receiver. In those implementations that do not support queueing a
+ * signal with information to a thread (such as POSIX), the signal may
+ * be sent to any thread that is waiting for this signal via
+ * fosa_signal_wait(). Portability can be ensured by having the receiver
+ * thread be the one who is waiting for the signal. 
+ *
+ * In this implementation, this limitation has been overcome by means
+ * of the Linux specific capability of sending a timer event directly
+ * to a specific thread. Thus, we program a fake timer to fire immediately
+ * and notify such event to the requested receiver thread.
+ *
+ * Returns 0 if successful; otherwise it returns an error code:
+ *     EINVAL: the signal specified by signal is not
+ *              between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX
+ *
+ *     EAGAIN: no resources are available to queue the signal; the
+ *             maximum number of queued signals has been reached, or a
+ *             systemwide resource limit has been exceeded
+ *
+ * Alternatively, in case of error the implementation is allowed to
+ * notify it to the system console and then terminate the FRSH
+ * implementation and dependant applications
+ **/
 int fosa_signal_queue(fosa_signal_t signal,
-       fosa_signal_info_t info,
-       fosa_thread_id_t receiver)
+                     fosa_signal_info_t info,
+                     fosa_thread_id_t receiver)
 {
-       union sigval siginfo;
+       int ret;
+       timer_t fake_timer;
+       struct itimerspec fake_time;
+       struct sigevent fake_event;
+
+       ret = timer_create(FOSA_CLOCK_REALTIME, &fake_event, &fake_timer);
+       if (ret) goto err;
+
+        fake_time.it_value.tv_sec = fake_time.it_value.tv_nsec = 0;
+        fake_time.it_interval.tv_sec = fake_time.it_interval.tv_nsec = 0;
+        fake_event.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL;
+        fake_event.sigev_signo  = SIGRTMIN;
+       fake_event.sigev_value.sival_int = info.sival_int;
+        fake_event._sigev_un._tid = receiver.linux_tid;
+
+        ret = timer_settime(fake_timer, TIMER_ABSTIME, &fake_time, NULL);
+       if (ret) {
+               timer_delete(fake_timer);
+               goto err;
+       }
+
+       ret = timer_delete(fake_timer);
+       if (ret) goto err;
 
-       siginfo.sival_int = info.sival_int;
-       return sigqueue(receiver.linux_pid, signal, siginfo);
+       return 0;
+err:
+       return errno;
 }
 
-int fosa_signal_wait (fosa_signal_t set[],
-       int size,
-       fosa_signal_t *signal_received,
-       fosa_signal_info_t *info)
+/**
+ * fosa_signal_wait()
+ *
+ * Wait for a signal
+ * 
+ * The function waits for the arrival of one of the signals in the
+ * array of signal numbers specified by set, which must be of size
+ * equal to size. If there is a signal already queued, the function
+ * returns immediately. If there is no signal of the specified set
+ * queued, the calling thread is suspended until a signal from that
+ * set arrives. Upon return, if signal_received is not NULL the number
+ * of the signal received is stored in the variable pointed to by
+ * signal_received; and if info is not NULL the associated information
+ * is stored in the variable pointed to by info.
+ *
+ * Returns 0 if successful; otherwise it returns an error code:
+ *     EINVAL: the array contains one or more values which are not
+ *             between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX, or size
+ *             is less than 0
+ *
+ * Alternatively, in case of error the implementation is allowed to
+ * notify it to the system console and then terminate the FRSH
+ * implementation and dependant applications
+ **/
+int fosa_signal_wait(fosa_signal_t set[], int size,
+                    fosa_signal_t *signal_received,
+                    fosa_signal_info_t *info)
 {
-       int x;
-       sigset_t wait_mask;
-       siginfo_t recv_signal_info;
-
-       /* only wait for the signals in 'set' */
-       sigemptyset(&wait_mask);
-       for (x = 0; x < size; x++)
-               sigaddset(&wait_mask, set[x]);
-       /* go to sleep and let's hope someone wake up us !! */
-       if (sigwaitinfo(&wait_mask, &recv_signal_info) < 0)
-               return -1;
-       /* return back informations for the caller */
-       *signal_received = recv_signal_info.si_signo;
-       info->sival_int = recv_signal_info.si_value.sival_int;
-       /* reset the signal mask to original one (orrible hack!!) */
-       pthread_sigmask(SIG_SETMASK, &original_mask, NULL);
+       int i, ret;
+       sigset_t sigset;
+       siginfo_t siginfo;
+
+       ret = sigemptyset(&sigset);
+       if (ret) goto err;
+
+       for (i = 0; i < size; i++) {
+               ret = sigaddset(&sigset,set[i]);
+               if (ret) goto err;
+       }
+
+       ret = sigwaitinfo(&sigset, &siginfo);
+       if (ret) goto err;
+
+       if (info != NULL && signal_received != NULL)
+               *signal_received = siginfo.si_signo;
+       if (info != NULL)
+               *info = (fosa_signal_info_t) siginfo.si_value.sival_int;
 
        return 0;
+err:
+       return errno;
 }
 
-int fosa_signal_timedwait(fosa_signal_t set[],
-       int size,
-       fosa_signal_t *signal_received,
-       fosa_signal_info_t *info,
-       const struct timespec *timeout)
+/**
+ * fosa_signal_timedwait()
+ *
+ * Timed wait for a signal
+ * 
+ * This function behaves the same as fosa_signal_wait(), except that
+ * the suspension time is limited to the time interval specified in
+ * the timespec structure referenced by timeout.
+ * 
+ * Returns 0 if successful; otherwise it returns an error code:
+ *     EINVAL: the array contains one or more values which are not
+ *             between FOSA_SIGNAL_MIN and FOSA_SIGNAL_MAX, or size
+ *             is less than 0, or timeout is invalid
+ *     EAGAIN: The timeout expired
+ *
+ * Alternatively, in case of error the implementation is allowed to
+ * notify it to the system console and then terminate the FRSH
+ * implementation and dependant applications
+ **/
+int fosa_signal_timedwait(fosa_signal_t set[], int size,
+                         fosa_signal_t *signal_received,
+                         fosa_signal_info_t *info,
+                         const struct timespec *timeout)
 {
-       int x;
-       sigset_t wait_mask;
-       siginfo_t recv_signal_info;
-
-       /* only wait for the signals in 'set' */
-       sigemptyset(&wait_mask);
-       for (x = 0; x < size; x++)
-               sigaddset(&wait_mask, set[x]);
-       /* go to sleep and let's hope someone wake up us !! */
-       if (sigtimedwait(&wait_mask, &recv_signal_info, timeout) < 0)
-               return -1;
-       /* return back informations for the caller */
+       int i, ret;
+       sigset_t signalset;
+       siginfo_t siginfo;
+
+       ret = sigemptyset(&signalset);
+       if (ret) goto err;
+
+       for (i = 0; i < size; i++) {
+               ret = sigaddset(&signalset,set[i]);
+               if (ret) goto err;
+       }
+
+       ret = sigtimedwait(&signalset,&siginfo,timeout);
+       if (ret) goto err;
+
        if (signal_received != NULL)
-               *signal_received = recv_signal_info.si_signo;
+               *signal_received = siginfo.si_signo;
        if (info != NULL)
-               info->sival_int = recv_signal_info.si_value.sival_int;
-       /* reset the signal mask to original one (orrible hack!!) */
-       pthread_sigmask(SIG_SETMASK, &original_mask, NULL);
+               *info = (fosa_signal_info_t) siginfo.si_value.sival_int;
 
-       return 0; 
+       return 0;
+err:
+       return errno;
 }