add break lock functionality to sosd
[archipelago] / xseg / drivers / user / xseg_pthread.c
index 1cc25aa..1c1fc53 100644 (file)
 #include <sys/util.h>
 #include <xseg/xseg.h>
 #include <pthread.h>
-
+#include <drivers/xseg_pthread.h>
 #define ERRSIZE 512
 char errbuf[ERRSIZE];
 
+static void *pthread_malloc(uint64_t size);
+static void pthread_mfree(void *mem);
+
 static long pthread_allocate(const char *name, uint64_t size)
 {
        int fd, r;
@@ -98,32 +101,34 @@ static void handler(int signum)
        counter ++;
 }
 
-static sigset_t savedset, set;
-static pthread_key_t key;
+static pthread_key_t pid_key, xpidx_key;
+static pthread_key_t mask_key, act_key;
 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
 static pthread_once_t once_quit = PTHREAD_ONCE_INIT;
 static int isInit;
 
-static void signal_init(void) 
+static void keys_init(void) 
 {
-       void (*h)(int);
        int r;
-       h = signal(SIGIO, handler);
-       if (h == SIG_ERR){
+       
+       r = pthread_key_create(&pid_key, NULL);
+       if (r < 0) {
                isInit = 0;
                return;
        }
-
-       sigemptyset(&set);
-       sigaddset(&set, SIGIO);
-
-       r = pthread_sigmask(SIG_BLOCK, &set, &savedset);
+       
+       r = pthread_key_create(&xpidx_key, NULL);
+       if (r < 0) {
+               isInit = 0;
+               return;
+       }
+       r = pthread_key_create(&mask_key, NULL);
        if (r < 0) {
                isInit = 0;
                return;
        }
        
-       r = pthread_key_create(&key, NULL);
+       r = pthread_key_create(&act_key, NULL);
        if (r < 0) {
                isInit = 0;
                return;
@@ -132,15 +137,6 @@ static void signal_init(void)
        once_quit = PTHREAD_ONCE_INIT;
 }
 
-static void signal_quit(void) 
-{
-       pthread_key_delete(key);
-       signal(SIGIO, SIG_DFL);
-       pthread_sigmask(SIG_SETMASK, &savedset, NULL);
-       isInit = 0;
-       once_init = PTHREAD_ONCE_INIT;
-}
-
 #define INT_TO_POINTER(__myptr, __myint) \
        do {\
                unsigned long __foo__ = (unsigned long) __myint; \
@@ -154,40 +150,117 @@ static void signal_quit(void)
        } while (0)
 
 /* must be called by each thread */
-static int pthread_signal_init(void)
+static int pthread_local_signal_init(struct xseg *xseg, xport portno)
 {
        int r;
        pid_t pid;
        void *tmp;
-       pthread_once(&once_init, signal_init);
+       sigset_t *savedset, *set;
+       struct sigaction *act, *old_act;
+
+       savedset = pthread_malloc(sizeof(sigset_t));
+       if (!savedset)
+               goto err1;
+       set = pthread_malloc(sizeof(sigset_t));
+       if (!set)
+               goto err2;
+
+       act = pthread_malloc(sizeof(struct sigaction));
+       if (!act)
+               goto err3;
+       old_act = pthread_malloc(sizeof(struct sigaction));
+       if (!old_act)
+               goto err4;
+
+       pthread_once(&once_init, keys_init);
        if (!isInit)
-               return -1;
+               goto err5;
+
+       sigemptyset(set);
+       act->sa_handler = handler;
+       act->sa_mask = *set;
+       act->sa_flags = 0;
+       if(sigaction(SIGIO, act, old_act) < 0)
+               goto err5;
+
+       
+       sigaddset(set, SIGIO);
+
+       r = pthread_sigmask(SIG_BLOCK, set, savedset);
+       if (r < 0) 
+               goto err6;
+
+
        pid = syscall(SYS_gettid);
        INT_TO_POINTER(tmp, pid);
-       r = pthread_setspecific(key, tmp);
-       return r;
+       if (!pthread_setspecific(pid_key, tmp) ||
+                       pthread_setspecific(mask_key, savedset) ||
+                       pthread_setspecific(act_key, old_act))
+               goto err7;
+
+       return 0;
+
+err7:
+       pthread_sigmask(SIG_BLOCK, savedset, NULL);
+err6:
+       sigaction(SIGIO, old_act, NULL);
+err5:
+       pthread_mfree(old_act);
+err4:
+       pthread_mfree(act);
+err3:
+       pthread_mfree(set);
+err2:
+       pthread_mfree(savedset);
+err1:
+       return -1;
+}
+
+/* should be called by each thread which had initialized signals */
+static void pthread_local_signal_quit(struct xseg *xseg, xport portno)
+{
+       sigset_t *savedset;
+       struct sigaction *old_act;
+
+       savedset = pthread_getspecific(act_key);
+       old_act = pthread_getspecific(mask_key);
+       if (old_act)
+               sigaction(SIGIO, old_act, NULL);
+       if (savedset)
+               pthread_sigmask(SIG_SETMASK, savedset, NULL);
+}
+
+static int pthread_remote_signal_init(void)
+{
+       return 0;
 }
 
-/* can be called by each thread */
-static void pthread_signal_quit(void)
+static void pthread_remote_signal_quit(void)
 {
-       pthread_once(&once_quit, signal_quit);
+       return;
 }
 
 static int pthread_prepare_wait(struct xseg *xseg, uint32_t portno)
 {
        void * tmp;
        pid_t pid;
+       xpool_index r;
        struct xseg_port *port = xseg_get_port(xseg, portno);
        if (!port) 
                return -1;
+       struct pthread_signal_desc *psd = xseg_get_signal_desc(xseg, port);
+       if (!psd)
+               return -1;
 
-       tmp = pthread_getspecific(key);
+       tmp = pthread_getspecific(pid_key);
        POINTER_TO_INT(pid, tmp);
        if (!pid)
                return -1;
 
-       xpool_add(&port->waiters, (xpool_index) pid, portno); 
+       r = xpool_add(&psd->waiters, (xpool_index) pid, portno); 
+       if (r == NoIndex)
+               return -1;
+       pthread_setspecific(xpidx_key, (void *)r);
        return 0;
 }
 
@@ -196,16 +269,25 @@ static int pthread_cancel_wait(struct xseg *xseg, uint32_t portno)
        void * tmp;
        pid_t pid;
        xpool_data data;
+       xpool_index xpidx, r;
        struct xseg_port *port = xseg_get_port(xseg, portno);
        if (!port) 
                return -1;
+       struct pthread_signal_desc *psd = xseg_get_signal_desc(xseg, port);
+       if (!psd)
+               return -1;
        
-       tmp = pthread_getspecific(key);
+       tmp = pthread_getspecific(pid_key);
        POINTER_TO_INT(pid, tmp);
        if (!pid)
                return -1;
 
-       xpool_remove(&port->waiters, (xpool_index) pid, &data, portno);
+       xpidx = (xpool_index) pthread_getspecific(xpidx_key);
+
+       r = xpool_remove(&psd->waiters, xpidx, &data, portno);
+       if (r == NoIndex)
+               return -1;
+       
        return 0;
 }
 
@@ -214,13 +296,13 @@ static int pthread_wait_signal(struct xseg *xseg, uint32_t usec_timeout)
        int r;
        siginfo_t siginfo;
        struct timespec ts;
+       sigset_t set;
+       sigemptyset(&set);
+       sigaddset(&set, SIGIO);
 
        ts.tv_sec = usec_timeout / 1000000;
        ts.tv_nsec = 1000 * (usec_timeout - ts.tv_sec * 1000000);
 
-       /* FIXME: Now that pthread signaling is fixed, we could get rid of the timeout
-        * and use a NULL timespec linux-specific)
-        */
        r = sigtimedwait(&set, &siginfo, &ts);
        if (r < 0)
                return r;
@@ -236,10 +318,13 @@ static int pthread_signal(struct xseg *xseg, uint32_t portno)
        struct xseg_port *port = xseg_get_port(xseg, portno);
        if (!port) 
                return -1;
+       struct pthread_signal_desc *psd = xseg_get_signal_desc(xseg, port);
+       if (!psd)
+               return -1;
 
-       idx = xpool_peek(&port->waiters, &data, portno); //FIXME portno is not the caller but the callee
+       idx = xpool_peek(&psd->waiters, &data, portno); //FIXME portno is not the caller but the callee
        if (idx == NoIndex) 
-               return -1;
+               return 0;
 
        pid_t cue = (pid_t) data;
        if (!cue)
@@ -276,11 +361,70 @@ static struct xseg_type xseg_pthread = {
        "pthread"
 };
 
+int pthread_init_signal_desc(struct xseg *xseg, void *sd)
+{       
+       struct pthread_signal_desc *psd = (struct pthread_signal_desc *)sd;
+       xpool_init(&psd->waiters, MAX_WAITERS, &psd->bufs);
+       xpool_clear(&psd->waiters, 1);
+       return 0;
+}       
+
+void pthread_quit_signal_desc(struct xseg *xseg, void *sd)
+{       
+       struct pthread_signal_desc *psd = (struct pthread_signal_desc *)sd;
+       xpool_clear(&psd->waiters, 1);
+       return;
+}           
+
+void * pthread_alloc_data(struct xseg *xseg)
+{
+       struct xobject_h *sd_h = xseg_get_objh(xseg, MAGIC_PTHREAD_SD,
+                               sizeof(struct pthread_signal_desc));
+       return sd_h;
+}
+
+void pthread_free_data(struct xseg *xseg, void *data)
+{
+       if (data)
+               xseg_put_objh(xseg, (struct xobject_h *)data);
+}
+
+void *pthread_alloc_signal_desc(struct xseg *xseg, void *data)
+{
+       struct xobject_h *sd_h = (struct xobject_h *) data;
+       if (!sd_h)
+               return NULL;
+       struct pthread_signal_desc *psd = xobj_get_obj(sd_h, X_ALLOC);
+       if (!psd)
+               return NULL;
+       return psd;
+
+}
+
+void pthread_free_signal_desc(struct xseg *xseg, void *data, void *sd)
+{
+       struct xobject_h *sd_h = (struct xobject_h *) data;
+       if (!sd_h)
+               return;
+       if (sd)
+               xobj_put_obj(sd_h, sd);
+       return;
+}
+
+
 static struct xseg_peer xseg_peer_pthread = {
        /* xseg_peer_operations */
        {
-               .signal_init    = pthread_signal_init,
-               .signal_quit    = pthread_signal_quit,
+               .init_signal_desc   = pthread_init_signal_desc,
+               .quit_signal_desc   = pthread_quit_signal_desc,
+               .alloc_data         = pthread_alloc_data,
+               .free_data          = pthread_free_data,
+               .alloc_signal_desc  = pthread_alloc_signal_desc,
+               .free_signal_desc   = pthread_free_signal_desc,
+               .local_signal_init  = pthread_local_signal_init,
+               .local_signal_quit  = pthread_local_signal_quit,
+               .remote_signal_init = pthread_remote_signal_init,
+               .remote_signal_quit = pthread_remote_signal_quit,
                .prepare_wait   = pthread_prepare_wait,
                .cancel_wait    = pthread_cancel_wait,
                .wait_signal    = pthread_wait_signal,