Print this page
enable v8plus_call to be used in any thread


  14 #include <pthread.h>
  15 #include "v8plus_glue.h"
  16 
  17 __thread v8plus_errno_t _v8plus_errno;
  18 __thread char _v8plus_errmsg[V8PLUS_ERRMSG_LEN];
  19 
  20 typedef struct v8plus_uv_ctx {
  21         void *vuc_obj;
  22         void *vuc_ctx;
  23         void *vuc_result;
  24         v8plus_worker_f vuc_worker;
  25         v8plus_completion_f vuc_completion;
  26 } v8plus_uv_ctx_t;
  27 
  28 static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
  29     STAILQ_HEAD_INITIALIZER(_v8plus_callq);
  30 static pthread_mutex_t _v8plus_callq_mtx;
  31 static pthread_t _v8plus_uv_event_thread;
  32 static uv_async_t _v8plus_uv_async;
  33 







  34 typedef struct v8plus_async_call {






  35         void *vac_cop;
  36         const char *vac_name;








  37         const nvlist_t *vac_lp;
  38 
  39         pthread_cond_t vac_cv;
  40         pthread_mutex_t vac_mtx;
  41 
  42         boolean_t vac_run;
  43         nvlist_t *vac_return;
  44 
  45         STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
  46 } v8plus_async_call_t;
  47 
  48 boolean_t
  49 v8plus_in_event_thread(void)
  50 {
  51         return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
  52 }
  53 
  54 static void
  55 v8plus_async_callback(uv_async_t *async, int status __UNUSED)
  56 {
  57         if (v8plus_in_event_thread() != B_TRUE)
  58                 v8plus_panic("async callback called outside of event loop");
  59 
  60         for (;;) {
  61                 v8plus_async_call_t *vac = NULL;
  62 
  63                 /*
  64                  * Fetch the next queued method:
  65                  */
  66                 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
  67                         v8plus_panic("could not lock async queue mutex");
  68                 if (!STAILQ_EMPTY(&_v8plus_callq)) {
  69                         vac = STAILQ_FIRST(&_v8plus_callq);
  70                         STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
  71                 }
  72                 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
  73                         v8plus_panic("could not unlock async queue mutex");
  74 
  75                 if (vac == NULL)
  76                         break;
  77 
  78                 /*
  79                  * Run the queued method:
  80                  */
  81                 if (vac->vac_run == B_TRUE)
  82                         v8plus_panic("async call already run");
  83                 vac->vac_return = v8plus_method_call_direct(vac->vac_cop,
  84                     vac->vac_name, vac->vac_lp);



























  85 
  86                 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
  87                         v8plus_panic("could not lock async call mutex");
  88                 vac->vac_run = B_TRUE;
  89                 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
  90                         v8plus_panic("could not signal async call condvar");
  91                 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
  92                         v8plus_panic("could not unlock async call mutex");
  93         }
  94 }
  95 
  96 nvlist_t *
  97 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
  98 {
  99         v8plus_async_call_t vac;
 100 
 101         if (v8plus_in_event_thread() == B_TRUE) {
 102                 /*
 103                  * We're running in the event loop thread, so we can make the
 104                  * call directly.
 105                  */
 106                 return (v8plus_method_call_direct(cop, name, lp));
 107         }
 108 
 109         /*
 110          * As we cannot manipulate v8plus/V8/Node structures directly from
 111          * outside the event loop thread, we push the call arguments onto a
 112          * queue and post to the event loop thread.  We then sleep on our
 113          * condition variable until the event loop thread makes the call
 114          * for us and wakes us up.
 115          */
 116         bzero(&vac, sizeof (vac));
 117         vac.vac_cop = cop;
 118         vac.vac_name = name;
 119         vac.vac_lp = lp;
 120         if (pthread_mutex_init(&vac.vac_mtx, NULL) != 0)
 121                 v8plus_panic("could not init async call mutex");
 122         if (pthread_cond_init(&vac.vac_cv, NULL) != 0)
 123                 v8plus_panic("could not init async call condvar");
 124         vac.vac_run = B_FALSE;
 125 
 126         /*
 127          * Post request to queue:
 128          */
 129         if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
 130                 v8plus_panic("could not lock async queue mutex");
 131         STAILQ_INSERT_TAIL(&_v8plus_callq, &vac, vac_callq_entry);
 132         if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
 133                 v8plus_panic("could not unlock async queue mutex");
 134         uv_async_send(&_v8plus_uv_async);
 135 









 136         /*
 137          * Wait for our request to be serviced on the event loop thread:
 138          */
 139         if (pthread_mutex_lock(&vac.vac_mtx) != 0)
 140                 v8plus_panic("could not lock async call mutex");
 141         while (vac.vac_run == B_FALSE) {
 142                 if (pthread_cond_wait(&vac.vac_cv, &vac.vac_mtx) != 0)
 143                         v8plus_panic("could not wait on async call condvar");
 144         }
 145         if (pthread_mutex_unlock(&vac.vac_mtx) != 0)
 146                 v8plus_panic("could not unlock async call mutex");
 147 
 148         if (pthread_cond_destroy(&vac.vac_cv) != 0)
 149                 v8plus_panic("could not destroy async call condvar");
 150         if (pthread_mutex_destroy(&vac.vac_mtx) != 0)
 151                 v8plus_panic("could not destroy async call mutex");
 152 
 153         return (vac.vac_return);























 154 }
 155 





























































 156 
 157 /*
 158  * Initialise structures for off-event-loop method calls.
 159  *
 160  * Note that uv_async_init() must be called inside the libuv event loop, so we
 161  * do it here.  We also want to record the thread ID of the Event Loop thread
 162  * so as to determine what kind of method calls to make later.
 163  */
 164 void
 165 v8plus_crossthread_init(void)
 166 {
 167         _v8plus_uv_event_thread = pthread_self();
 168         if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
 169             v8plus_async_callback) != 0)
 170                 v8plus_panic("unable to initialise uv_async_t");
 171         if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
 172                 v8plus_panic("unable to initialise mutex");
 173 }
 174 
 175 nvlist_t *




  14 #include <pthread.h>
  15 #include "v8plus_glue.h"
  16 
  17 __thread v8plus_errno_t _v8plus_errno;
  18 __thread char _v8plus_errmsg[V8PLUS_ERRMSG_LEN];
  19 
  20 typedef struct v8plus_uv_ctx {
  21         void *vuc_obj;
  22         void *vuc_ctx;
  23         void *vuc_result;
  24         v8plus_worker_f vuc_worker;
  25         v8plus_completion_f vuc_completion;
  26 } v8plus_uv_ctx_t;
  27 
  28 static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
  29     STAILQ_HEAD_INITIALIZER(_v8plus_callq);
  30 static pthread_mutex_t _v8plus_callq_mtx;
  31 static pthread_t _v8plus_uv_event_thread;
  32 static uv_async_t _v8plus_uv_async;
  33 
  34 typedef enum v8plus_async_call_type {
  35         ACT_OBJECT_CALL = 1,
  36         ACT_OBJECT_RELEASE,
  37         ACT_JSFUNC_CALL,
  38         ACT_JSFUNC_RELEASE,
  39 } v8plus_async_call_type_t;
  40 
  41 typedef struct v8plus_async_call {
  42         v8plus_async_call_type_t vac_type;
  43         boolean_t vac_noreply;
  44 
  45         /*
  46          * For ACT_OBJECT_{CALL,RELEASE}:
  47          */
  48         void *vac_cop;
  49         const char *vac_name;
  50         /*
  51          * For ACT_JSFUNC_{CALL,RELEASE}:
  52          */
  53         v8plus_jsfunc_t vac_func;
  54 
  55         /*
  56          * Common call arguments:
  57          */
  58         const nvlist_t *vac_lp;
  59 
  60         pthread_cond_t vac_cv;
  61         pthread_mutex_t vac_mtx;
  62 
  63         boolean_t vac_run;
  64         nvlist_t *vac_return;
  65 
  66         STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
  67 } v8plus_async_call_t;
  68 
  69 boolean_t
  70 v8plus_in_event_thread(void)
  71 {
  72         return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
  73 }
  74 
  75 static void
  76 v8plus_async_callback(uv_async_t *async __UNUSED, int status __UNUSED)
  77 {
  78         if (v8plus_in_event_thread() != B_TRUE)
  79                 v8plus_panic("async callback called outside of event loop");
  80 
  81         for (;;) {
  82                 v8plus_async_call_t *vac = NULL;
  83 
  84                 /*
  85                  * Fetch the next queued method:
  86                  */
  87                 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
  88                         v8plus_panic("could not lock async queue mutex");
  89                 if (!STAILQ_EMPTY(&_v8plus_callq)) {
  90                         vac = STAILQ_FIRST(&_v8plus_callq);
  91                         STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
  92                 }
  93                 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
  94                         v8plus_panic("could not unlock async queue mutex");
  95 
  96                 if (vac == NULL)
  97                         break;
  98 
  99                 /*
 100                  * Run the queued method:
 101                  */
 102                 if (vac->vac_run == B_TRUE)
 103                         v8plus_panic("async call already run");
 104 
 105                 switch (vac->vac_type) {
 106                 case ACT_OBJECT_CALL:
 107                         vac->vac_return = v8plus_method_call_direct(
 108                             vac->vac_cop, vac->vac_name, vac->vac_lp);
 109                         break;
 110                 case ACT_OBJECT_RELEASE:
 111                         v8plus_obj_rele(vac->vac_cop);
 112                         break;
 113                 case ACT_JSFUNC_CALL:
 114                         vac->vac_return = v8plus_call_direct(
 115                             vac->vac_func, vac->vac_lp);
 116                         break;
 117                 case ACT_JSFUNC_RELEASE:
 118                         v8plus_jsfunc_rele(vac->vac_func);
 119                         break;
 120                 }
 121 
 122                 if (vac->vac_noreply == B_TRUE) {
 123                         /*
 124                          * The caller posted this event and is not sleeping
 125                          * on a reply.  Just free the call structure and move
 126                          * on.
 127                          */
 128                         free(vac);
 129                         if (vac->vac_lp != NULL)
 130                                 nvlist_free((nvlist_t *)vac->vac_lp);
 131                         continue;
 132                 }
 133 
 134                 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
 135                         v8plus_panic("could not lock async call mutex");
 136                 vac->vac_run = B_TRUE;
 137                 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
 138                         v8plus_panic("could not signal async call condvar");
 139                 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
 140                         v8plus_panic("could not unlock async call mutex");
 141         }
 142 }
 143 
 144 /*
 145  * As we cannot manipulate v8plus/V8/Node structures directly from outside the
 146  * event loop thread, we push the call arguments onto a queue and post to the
 147  * event loop thread.  We then sleep on our condition variable until the event
 148  * loop thread makes the call for us and wakes us up.
 149  *
 150  * This routine implements the parts of this interaction common to all
 151  * variants.

 152  */
 153 static nvlist_t *
 154 v8plus_cross_thread_call(v8plus_async_call_t *vac)
 155 {
 156         /*
 157          * Common call structure initialisation:




 158          */
 159         if (pthread_mutex_init(&vac->vac_mtx, NULL) != 0)




 160                 v8plus_panic("could not init async call mutex");
 161         if (pthread_cond_init(&vac->vac_cv, NULL) != 0)
 162                 v8plus_panic("could not init async call condvar");
 163         vac->vac_run = B_FALSE;
 164 
 165         /*
 166          * Post request to queue:
 167          */
 168         if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
 169                 v8plus_panic("could not lock async queue mutex");
 170         STAILQ_INSERT_TAIL(&_v8plus_callq, vac, vac_callq_entry);
 171         if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
 172                 v8plus_panic("could not unlock async queue mutex");
 173         uv_async_send(&_v8plus_uv_async);
 174 
 175         if (vac->vac_noreply == B_TRUE) {
 176                 /*
 177                  * The caller does not care about the reply, and has allocated
 178                  * the v8plus_async_call_t structure from the heap.  The
 179                  * async callback will free the storage when it completes.
 180                  */
 181                 return (NULL);
 182         }
 183 
 184         /*
 185          * Wait for our request to be serviced on the event loop thread:
 186          */
 187         if (pthread_mutex_lock(&vac->vac_mtx) != 0)
 188                 v8plus_panic("could not lock async call mutex");
 189         while (vac->vac_run == B_FALSE) {
 190                 if (pthread_cond_wait(&vac->vac_cv, &vac->vac_mtx) != 0)
 191                         v8plus_panic("could not wait on async call condvar");
 192         }
 193         if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
 194                 v8plus_panic("could not unlock async call mutex");
 195 
 196         if (pthread_cond_destroy(&vac->vac_cv) != 0)
 197                 v8plus_panic("could not destroy async call condvar");
 198         if (pthread_mutex_destroy(&vac->vac_mtx) != 0)
 199                 v8plus_panic("could not destroy async call mutex");
 200 
 201         return (vac->vac_return);
 202 }
 203 
 204 nvlist_t *
 205 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
 206 {
 207         v8plus_async_call_t vac;
 208 
 209         if (v8plus_in_event_thread() == B_TRUE) {
 210                 /*
 211                  * We're running in the event loop thread, so we can make the
 212                  * call directly.
 213                  */
 214                 return (v8plus_method_call_direct(cop, name, lp));
 215         }
 216 
 217         bzero(&vac, sizeof (vac));
 218         vac.vac_type = ACT_OBJECT_CALL;
 219         vac.vac_noreply = B_FALSE;
 220         vac.vac_cop = cop;
 221         vac.vac_name = name;
 222         vac.vac_lp = lp;
 223 
 224         return (v8plus_cross_thread_call(&vac));
 225 }
 226 
 227 nvlist_t *
 228 v8plus_call(v8plus_jsfunc_t func, const nvlist_t *lp)
 229 {
 230         v8plus_async_call_t vac;
 231 
 232         if (v8plus_in_event_thread() == B_TRUE) {
 233                 /*
 234                  * We're running in the event loop thread, so we can make the
 235                  * call directly.
 236                  */
 237                 return (v8plus_call_direct(func, lp));
 238         }
 239 
 240         bzero(&vac, sizeof (vac));
 241         vac.vac_type = ACT_JSFUNC_CALL;
 242         vac.vac_noreply = B_FALSE;
 243         vac.vac_func = func;
 244         vac.vac_lp = lp;
 245 
 246         return (v8plus_cross_thread_call(&vac));
 247 }
 248 
 249 void
 250 v8plus_obj_rele(const void *cop)
 251 {
 252         v8plus_async_call_t *vac;
 253 
 254         if (v8plus_in_event_thread() == B_TRUE) {
 255                 return (v8plus_obj_rele_direct(cop));
 256         }
 257 
 258         vac = calloc(1, sizeof (*vac));
 259         if (vac == NULL)
 260                 v8plus_panic("could not allocate async call structure");
 261 
 262         vac->vac_type = ACT_OBJECT_RELEASE;
 263         vac->vac_noreply = B_TRUE;
 264         vac->vac_cop = (void *)cop;
 265 
 266         (void) v8plus_cross_thread_call(vac);
 267 }
 268 
 269 void
 270 v8plus_jsfunc_rele(v8plus_jsfunc_t f)
 271 {
 272         v8plus_async_call_t *vac;
 273 
 274         if (v8plus_in_event_thread() == B_TRUE) {
 275                 return (v8plus_jsfunc_rele_direct(f));
 276         }
 277 
 278         vac = calloc(1, sizeof (*vac));
 279         if (vac == NULL)
 280                 v8plus_panic("could not allocate async call structure");
 281 
 282         vac->vac_type = ACT_JSFUNC_RELEASE;
 283         vac->vac_noreply = B_TRUE;
 284         vac->vac_func = f;
 285 
 286         (void) v8plus_cross_thread_call(vac);
 287 }
 288 
 289 /*
 290  * Initialise structures for off-event-loop method calls.
 291  *
 292  * Note that uv_async_init() must be called inside the libuv event loop, so we
 293  * do it here.  We also want to record the thread ID of the Event Loop thread
 294  * so as to determine what kind of method calls to make later.
 295  */
 296 void
 297 v8plus_crossthread_init(void)
 298 {
 299         _v8plus_uv_event_thread = pthread_self();
 300         if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
 301             v8plus_async_callback) != 0)
 302                 v8plus_panic("unable to initialise uv_async_t");
 303         if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
 304                 v8plus_panic("unable to initialise mutex");
 305 }
 306 
 307 nvlist_t *