1 /*
   2  * Copyright (c) 2012 Joyent, Inc.  All rights reserved.
   3  */
   4 
   5 #include <sys/ccompile.h>
   6 #include <sys/debug.h>
   7 #include <sys/queue.h>
   8 #include <sys/types.h>
   9 #include <stdarg.h>
  10 #include <string.h>
  11 #include <strings.h>
  12 #include <errno.h>
  13 #include <uv.h>
  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 *
 176 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
 177 {
 178         if (fmt == NULL) {
 179                 if (e == V8PLUSERR_NOERROR) {
 180                         *_v8plus_errmsg = '\0';
 181                 } else {
 182                         (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
 183                             "%s", v8plus_strerror(e));
 184                 }
 185         } else {
 186                 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap);
 187         }
 188         _v8plus_errno = e;
 189 
 190         return (NULL);
 191 }
 192 
 193 nvlist_t *
 194 v8plus_error(v8plus_errno_t e, const char *fmt, ...)
 195 {
 196         va_list ap;
 197 
 198         va_start(ap, fmt);
 199         (void) v8plus_verror(e, fmt, ap);
 200         va_end(ap);
 201 
 202         return (NULL);
 203 }
 204 
 205 static void __NORETURN
 206 v8plus_vpanic(const char *fmt, va_list ap)
 207 {
 208         (void) vfprintf(stderr, fmt, ap);
 209         (void) fflush(stderr);
 210         abort();
 211 }
 212 
 213 void
 214 v8plus_panic(const char *fmt, ...)
 215 {
 216         va_list ap;
 217 
 218         va_start(ap, fmt);
 219         v8plus_vpanic(fmt, ap);
 220         va_end(ap);
 221 }
 222 
 223 nvlist_t *
 224 v8plus_nverr(int nverr, const char *member)
 225 {
 226         (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
 227             "nvlist manipulation error on member %s: %s",
 228             member == NULL ? "<none>" : member, strerror(nverr));
 229 
 230         switch (nverr) {
 231         case ENOMEM:
 232                 _v8plus_errno = V8PLUSERR_NOMEM;
 233                 break;
 234         case EINVAL:
 235                 _v8plus_errno = V8PLUSERR_YOUSUCK;
 236                 break;
 237         default:
 238                 _v8plus_errno = V8PLUSERR_UNKNOWN;
 239                 break;
 240         }
 241 
 242         return (NULL);
 243 }
 244 
 245 nvlist_t *
 246 v8plus_syserr(int syserr, const char *fmt, ...)
 247 {
 248         v8plus_errno_t e;
 249         va_list ap;
 250 
 251         switch (syserr) {
 252         case ENOMEM:
 253                 e = V8PLUSERR_NOMEM;
 254                 break;
 255         case EBADF:
 256                 e = V8PLUSERR_BADF;
 257                 break;
 258         default:
 259                 e = V8PLUSERR_UNKNOWN;
 260                 break;
 261         }
 262 
 263         va_start(ap, fmt);
 264         (void) v8plus_verror(e, fmt, ap);
 265         va_end(ap);
 266 
 267         return (NULL);
 268 }
 269 
 270 /*
 271  * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void.
 272  */
 273 nvlist_t *
 274 v8plus_void(void)
 275 {
 276         return (v8plus_error(V8PLUSERR_NOERROR, NULL));
 277 }
 278 
 279 v8plus_type_t
 280 v8plus_typeof(const nvpair_t *pp)
 281 {
 282         data_type_t t = nvpair_type((nvpair_t *)pp);
 283 
 284         switch (t) {
 285         case DATA_TYPE_DOUBLE:
 286                 return (V8PLUS_TYPE_NUMBER);
 287         case DATA_TYPE_STRING:
 288                 return (V8PLUS_TYPE_STRING);
 289         case DATA_TYPE_NVLIST:
 290                 return (V8PLUS_TYPE_OBJECT);
 291         case DATA_TYPE_BOOLEAN_VALUE:
 292                 return (V8PLUS_TYPE_BOOLEAN);
 293         case DATA_TYPE_BOOLEAN:
 294                 return (V8PLUS_TYPE_UNDEFINED);
 295         case DATA_TYPE_BYTE:
 296         {
 297                 uchar_t v;
 298                 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0)
 299                         return (V8PLUS_TYPE_INVALID);
 300                 return (V8PLUS_TYPE_NULL);
 301         }
 302         case DATA_TYPE_UINT64_ARRAY:
 303         {
 304                 uint64_t *vp;
 305                 uint_t nv;
 306                 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 ||
 307                     nv != 1) {
 308                         return (V8PLUS_TYPE_INVALID);
 309                 }
 310                 return (V8PLUS_TYPE_JSFUNC);
 311         }
 312         default:
 313                 return (V8PLUS_TYPE_INVALID);
 314         }
 315 }
 316 
 317 static int
 318 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp)
 319 {
 320         data_type_t dt = nvpair_type((nvpair_t *)pp);
 321 
 322         switch (t) {
 323         case V8PLUS_TYPE_NONE:
 324                 return (-1);
 325         case V8PLUS_TYPE_STRING:
 326                 if (dt == DATA_TYPE_STRING) {
 327                         if (vp != NULL) {
 328                                 (void) nvpair_value_string((nvpair_t *)pp,
 329                                     (char **)vp);
 330                         }
 331                         return (0);
 332                 }
 333                 return (-1);
 334         case V8PLUS_TYPE_NUMBER:
 335                 if (dt == DATA_TYPE_DOUBLE) {
 336                         if (vp != NULL) {
 337                                 (void) nvpair_value_double((nvpair_t *)pp,
 338                                     (double *)vp);
 339                         }
 340                         return (0);
 341                 }
 342                 return (-1);
 343         case V8PLUS_TYPE_BOOLEAN:
 344                 if (dt == DATA_TYPE_BOOLEAN_VALUE) {
 345                         if (vp != NULL) {
 346                                 (void) nvpair_value_boolean_value(
 347                                     (nvpair_t *)pp, (boolean_t *)vp);
 348                         }
 349                         return (0);
 350                 }
 351                 return (-1);
 352         case V8PLUS_TYPE_JSFUNC:
 353                 if (dt == DATA_TYPE_UINT64_ARRAY) {
 354                         uint_t nv;
 355                         uint64_t *vpp;
 356 
 357                         if (nvpair_value_uint64_array((nvpair_t *)pp,
 358                             &vpp, &nv) == 0 && nv == 1) {
 359                                 if (vp != NULL)
 360                                         *(v8plus_jsfunc_t *)vp = vpp[0];
 361                                 return (0);
 362                         }
 363                 }
 364                 return (-1);
 365         case V8PLUS_TYPE_OBJECT:
 366                 if (dt == DATA_TYPE_NVLIST) {
 367                         if (vp != NULL) {
 368                                 (void) nvpair_value_nvlist((nvpair_t *)pp,
 369                                     (nvlist_t **)vp);
 370                         }
 371                         return (0);
 372                 }
 373                 return (-1);
 374         case V8PLUS_TYPE_NULL:
 375                 if (dt == DATA_TYPE_BYTE) {
 376                         uchar_t v;
 377 
 378                         if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 &&
 379                             v == 0)
 380                                 return (0);
 381                 }
 382                 return (-1);
 383         case V8PLUS_TYPE_UNDEFINED:
 384                 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1);
 385         case V8PLUS_TYPE_ANY:
 386                 if (vp != NULL)
 387                         *(const nvpair_t **)vp = pp;
 388                 return (0);
 389         case V8PLUS_TYPE_INVALID:
 390                 if (vp != NULL)
 391                         *(data_type_t *)vp = dt;
 392                 return (0);
 393         case V8PLUS_TYPE_STRNUMBER64:
 394                 if (dt == DATA_TYPE_STRING) {
 395                         char *s;
 396                         uint64_t v;
 397 
 398                         (void) nvpair_value_string((nvpair_t *)pp, &s);
 399                         errno = 0;
 400                         v = (uint64_t)strtoull(s, NULL, 0);
 401                         if (errno != 0)
 402                                 return (-1);
 403                         if (vp != NULL)
 404                                 *(uint64_t *)vp = v;
 405                         return (0);
 406                 }
 407                 return (-1);
 408         default:
 409                 return (-1);
 410         }
 411 }
 412 
 413 int
 414 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
 415 {
 416         v8plus_type_t nt;
 417         nvpair_t *pp;
 418         void *vp;
 419         va_list ap;
 420         uint_t i;
 421         char buf[32];
 422 
 423         va_start(ap, t);
 424 
 425         for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
 426                 switch (nt) {
 427                 case V8PLUS_TYPE_UNDEFINED:
 428                 case V8PLUS_TYPE_NULL:
 429                         break;
 430                 default:
 431                         (void) va_arg(ap, void *);
 432                 }
 433 
 434                 (void) snprintf(buf, sizeof (buf), "%u", i);
 435                 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) {
 436                         (void) v8plus_error(V8PLUSERR_MISSINGARG,
 437                             "argument %u is required", i);
 438                         return (-1);
 439                 }
 440 
 441                 if (v8plus_arg_value(nt, pp, NULL) != 0) {
 442                         (void) v8plus_error(V8PLUSERR_BADARG,
 443                             "argument %u is of incorrect type", i);
 444                         return (-1);
 445                 }
 446 
 447                 nt = va_arg(ap, data_type_t);
 448         }
 449 
 450         va_end(ap);
 451 
 452         if (flags & V8PLUS_ARG_F_NOEXTRA) {
 453                 (void) snprintf(buf, sizeof (buf), "%u", i);
 454                 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) {
 455                         (void) v8plus_error(V8PLUSERR_EXTRAARG,
 456                             "superfluous extra argument(s) detected");
 457                         return (-1);
 458                 }
 459         }
 460 
 461         va_start(ap, t);
 462 
 463         for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
 464                 switch (nt) {
 465                 case V8PLUS_TYPE_UNDEFINED:
 466                 case V8PLUS_TYPE_NULL:
 467                         vp = NULL;
 468                         break;
 469                 default:
 470                         vp = va_arg(ap, void *);
 471                 }
 472 
 473                 (void) snprintf(buf, sizeof (buf), "%u", i);
 474                 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0);
 475                 VERIFY(v8plus_arg_value(nt, pp, vp) == 0);
 476 
 477                 nt = va_arg(ap, data_type_t);
 478         }
 479 
 480         va_end(ap);
 481 
 482         return (0);
 483 }
 484 
 485 static int
 486 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap)
 487 {
 488         v8plus_type_t nt = t;
 489         char *name;
 490         int err;
 491 
 492         /*
 493          * Do not call va_start() or va_end() in this function!  We are limited
 494          * to a single traversal of the arguments so that we can recurse to
 495          * handle embedded object definitions.
 496          */
 497 
 498         while (nt != V8PLUS_TYPE_NONE) {
 499                 name = va_arg(*ap, char *);
 500 
 501                 switch (nt) {
 502                 case V8PLUS_TYPE_STRING:
 503                 {
 504                         char *s = va_arg(*ap, char *);
 505                         if ((err = nvlist_add_string(lp, name, s)) != 0) {
 506                                 (void) v8plus_nverr(err, name);
 507                                 return (-1);
 508                         }
 509                         break;
 510                 }
 511                 case V8PLUS_TYPE_NUMBER:
 512                 {
 513                         double d = va_arg(*ap, double);
 514                         if ((err = nvlist_add_double(lp, name, d)) != 0) {
 515                                 (void) v8plus_nverr(err, name);
 516                                 return (-1);
 517                         }
 518                         break;
 519                 }
 520                 case V8PLUS_TYPE_BOOLEAN:
 521                 {
 522                         boolean_t b = va_arg(*ap, boolean_t);
 523                         if ((err = nvlist_add_boolean_value(lp,
 524                             name, b)) != 0) {
 525                                 (void) v8plus_nverr(err, name);
 526                                 return (-1);
 527                         }
 528                         break;
 529                 }
 530                 case V8PLUS_TYPE_JSFUNC:
 531                 {
 532                         v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t);
 533                         if ((err = nvlist_add_uint64_array(lp,
 534                             name, &j, 1)) != 0) {
 535                                 (void) v8plus_nverr(err, name);
 536                                 return (-1);
 537                         }
 538                         if ((err = nvlist_add_string_array(lp,
 539                             V8PLUS_JSF_COOKIE, NULL, 0)) != 0) {
 540                                 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE);
 541                                 return (-1);
 542                         }
 543                         v8plus_jsfunc_hold(j);
 544                         break;
 545                 }
 546                 case V8PLUS_TYPE_OBJECT:
 547                 {
 548                         const nvlist_t *op = va_arg(*ap, const nvlist_t *);
 549                         if ((err = nvlist_add_nvlist(lp, name,
 550                             (nvlist_t *)op)) != 0) {
 551                                 (void) v8plus_nverr(err, name);
 552                                 return (-1);
 553                         }
 554                         break;
 555                 }
 556                 case V8PLUS_TYPE_NULL:
 557                         if ((err = nvlist_add_byte(lp, name, 0)) != 0) {
 558                                 (void) v8plus_nverr(err, name);
 559                                 return (-1);
 560                         }
 561                         break;
 562                 case V8PLUS_TYPE_UNDEFINED:
 563                         if ((err = nvlist_add_boolean(lp, name)) != 0) {
 564                                 (void) v8plus_nverr(err, name);
 565                                 return (-1);
 566                         }
 567                         break;
 568                 case V8PLUS_TYPE_ANY:
 569                 {
 570                         nvpair_t *pp = va_arg(*ap, nvpair_t *);
 571                         if ((err = nvlist_add_nvpair(lp, pp)) != 0) {
 572                                 (void) v8plus_nverr(err, name);
 573                                 return (-1);
 574                         }
 575                         break;
 576                 }
 577                 case V8PLUS_TYPE_STRNUMBER64:
 578                 {
 579                         uint64_t v = va_arg(*ap, uint64_t);
 580                         char s[32];
 581                         (void) snprintf(s, sizeof (s), "%" PRIu64, v);
 582                         if ((err = nvlist_add_string(lp, name, s)) != 0) {
 583                                 (void) v8plus_nverr(err, name);
 584                                 return (-1);
 585                         }
 586                         break;
 587                 }
 588                 case V8PLUS_TYPE_INL_OBJECT:
 589                 {
 590                         nvlist_t *slp;
 591 
 592                         nt = va_arg(*ap, v8plus_type_t);
 593                         err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0);
 594                         if (err != 0) {
 595                                 (void) v8plus_nverr(err, name);
 596                                 return (-1);
 597                         }
 598                         if (v8plus_obj_vsetprops(slp, nt, ap) != 0)
 599                                 return (-1);
 600 
 601                         err = nvlist_add_nvlist(lp, name, slp);
 602                         nvlist_free(slp);
 603                         if (err != 0) {
 604                                 (void) v8plus_nverr(err, name);
 605                                 return (-1);
 606                         }
 607                         break;
 608                 }
 609                 case V8PLUS_TYPE_INVALID:
 610                 default:
 611                         (void) v8plus_error(V8PLUSERR_YOUSUCK,
 612                             "invalid property type %d", nt);
 613                         return (-1);
 614                 }
 615 
 616                 nt = va_arg(*ap, v8plus_type_t);
 617         }
 618 
 619         return (0);
 620 }
 621 
 622 nvlist_t *
 623 v8plus_obj(v8plus_type_t t, ...)
 624 {
 625         nvlist_t *rp;
 626         va_list ap;
 627         int err;
 628 
 629         if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
 630                 return (v8plus_nverr(err, NULL));
 631 
 632         va_start(ap, t);
 633         err = v8plus_obj_vsetprops(rp, t, &ap);
 634         va_end(ap);
 635 
 636         if (err != 0) {
 637                 nvlist_free(rp);
 638                 rp = NULL;
 639         }
 640 
 641         return (rp);
 642 }
 643 
 644 int
 645 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
 646 {
 647         va_list ap;
 648         int err;
 649 
 650         va_start(ap, t);
 651         err = v8plus_obj_vsetprops(lp, t, &ap);
 652         va_end(ap);
 653 
 654         return (err);
 655 }
 656 
 657 static void
 658 v8plus_uv_worker(uv_work_t *wp)
 659 {
 660         v8plus_uv_ctx_t *cp = wp->data;
 661 
 662         cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx);
 663 }
 664 
 665 static void
 666 v8plus_uv_completion(uv_work_t *wp)
 667 {
 668         v8plus_uv_ctx_t *cp = wp->data;
 669 
 670         cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result);
 671         v8plus_obj_rele(cp->vuc_obj);
 672         free(cp);
 673         free(wp);
 674 }
 675 
 676 void
 677 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker,
 678     v8plus_completion_f completion)
 679 {
 680         uv_work_t *wp = malloc(sizeof (uv_work_t));
 681         v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t));
 682 
 683         bzero(wp, sizeof (uv_work_t));
 684         bzero(cp, sizeof (v8plus_uv_ctx_t));
 685 
 686         v8plus_obj_hold(cop);
 687         cp->vuc_obj = cop;
 688         cp->vuc_ctx = ctxp;
 689         cp->vuc_worker = worker;
 690         cp->vuc_completion = completion;
 691         wp->data = cp;
 692 
 693         uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker,
 694             v8plus_uv_completion);
 695 }