Print this page
enable v8plus_call to be used in any thread

Split Close
Expand all
Collapse all
          --- old/./v8plus_csup.c
          +++ new/./v8plus_csup.c
↓ open down ↓ 23 lines elided ↑ open up ↑
  24   24          v8plus_worker_f vuc_worker;
  25   25          v8plus_completion_f vuc_completion;
  26   26  } v8plus_uv_ctx_t;
  27   27  
  28   28  static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
  29   29      STAILQ_HEAD_INITIALIZER(_v8plus_callq);
  30   30  static pthread_mutex_t _v8plus_callq_mtx;
  31   31  static pthread_t _v8plus_uv_event_thread;
  32   32  static uv_async_t _v8plus_uv_async;
  33   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 +
  34   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 +         */
  35   48          void *vac_cop;
  36   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 +         */
  37   58          const nvlist_t *vac_lp;
  38   59  
  39   60          pthread_cond_t vac_cv;
  40   61          pthread_mutex_t vac_mtx;
  41   62  
  42   63          boolean_t vac_run;
  43   64          nvlist_t *vac_return;
  44   65  
  45   66          STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
  46   67  } v8plus_async_call_t;
  47   68  
  48   69  boolean_t
  49   70  v8plus_in_event_thread(void)
  50   71  {
  51   72          return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
  52   73  }
  53   74  
  54   75  static void
  55      -v8plus_async_callback(uv_async_t *async, int status __UNUSED)
       76 +v8plus_async_callback(uv_async_t *async __UNUSED, int status __UNUSED)
  56   77  {
  57   78          if (v8plus_in_event_thread() != B_TRUE)
  58   79                  v8plus_panic("async callback called outside of event loop");
  59   80  
  60   81          for (;;) {
  61   82                  v8plus_async_call_t *vac = NULL;
  62   83  
  63   84                  /*
  64   85                   * Fetch the next queued method:
  65   86                   */
↓ open down ↓ 7 lines elided ↑ open up ↑
  73   94                          v8plus_panic("could not unlock async queue mutex");
  74   95  
  75   96                  if (vac == NULL)
  76   97                          break;
  77   98  
  78   99                  /*
  79  100                   * Run the queued method:
  80  101                   */
  81  102                  if (vac->vac_run == B_TRUE)
  82  103                          v8plus_panic("async call already run");
  83      -                vac->vac_return = v8plus_method_call_direct(vac->vac_cop,
  84      -                    vac->vac_name, vac->vac_lp);
      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 +                }
  85  133  
  86  134                  if (pthread_mutex_lock(&vac->vac_mtx) != 0)
  87  135                          v8plus_panic("could not lock async call mutex");
  88  136                  vac->vac_run = B_TRUE;
  89  137                  if (pthread_cond_broadcast(&vac->vac_cv) != 0)
  90  138                          v8plus_panic("could not signal async call condvar");
  91  139                  if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
  92  140                          v8plus_panic("could not unlock async call mutex");
  93  141          }
  94  142  }
  95  143  
  96      -nvlist_t *
  97      -v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
      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)
  98  155  {
  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  156          /*
 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.
      157 +         * Common call structure initialisation:
 115  158           */
 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)
      159 +        if (pthread_mutex_init(&vac->vac_mtx, NULL) != 0)
 121  160                  v8plus_panic("could not init async call mutex");
 122      -        if (pthread_cond_init(&vac.vac_cv, NULL) != 0)
      161 +        if (pthread_cond_init(&vac->vac_cv, NULL) != 0)
 123  162                  v8plus_panic("could not init async call condvar");
 124      -        vac.vac_run = B_FALSE;
      163 +        vac->vac_run = B_FALSE;
 125  164  
 126  165          /*
 127  166           * Post request to queue:
 128  167           */
 129  168          if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
 130  169                  v8plus_panic("could not lock async queue mutex");
 131      -        STAILQ_INSERT_TAIL(&_v8plus_callq, &vac, vac_callq_entry);
      170 +        STAILQ_INSERT_TAIL(&_v8plus_callq, vac, vac_callq_entry);
 132  171          if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
 133  172                  v8plus_panic("could not unlock async queue mutex");
 134  173          uv_async_send(&_v8plus_uv_async);
 135  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 +
 136  184          /*
 137  185           * Wait for our request to be serviced on the event loop thread:
 138  186           */
 139      -        if (pthread_mutex_lock(&vac.vac_mtx) != 0)
      187 +        if (pthread_mutex_lock(&vac->vac_mtx) != 0)
 140  188                  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)
      189 +        while (vac->vac_run == B_FALSE) {
      190 +                if (pthread_cond_wait(&vac->vac_cv, &vac->vac_mtx) != 0)
 143  191                          v8plus_panic("could not wait on async call condvar");
 144  192          }
 145      -        if (pthread_mutex_unlock(&vac.vac_mtx) != 0)
      193 +        if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
 146  194                  v8plus_panic("could not unlock async call mutex");
 147  195  
 148      -        if (pthread_cond_destroy(&vac.vac_cv) != 0)
      196 +        if (pthread_cond_destroy(&vac->vac_cv) != 0)
 149  197                  v8plus_panic("could not destroy async call condvar");
 150      -        if (pthread_mutex_destroy(&vac.vac_mtx) != 0)
      198 +        if (pthread_mutex_destroy(&vac->vac_mtx) != 0)
 151  199                  v8plus_panic("could not destroy async call mutex");
 152  200  
 153      -        return (vac.vac_return);
      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));
 154  247  }
 155  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 +}
 156  288  
 157  289  /*
 158  290   * Initialise structures for off-event-loop method calls.
 159  291   *
 160  292   * Note that uv_async_init() must be called inside the libuv event loop, so we
 161  293   * do it here.  We also want to record the thread ID of the Event Loop thread
 162  294   * so as to determine what kind of method calls to make later.
 163  295   */
 164  296  void
 165  297  v8plus_crossthread_init(void)
↓ open down ↓ 530 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX