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 }