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 nvlist_t *v8plus_method_call_direct(void *, const char *, const nvlist_t *); 49 50 boolean_t 51 v8plus_in_event_thread(void) 52 { 53 return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE); 54 } 55 56 static void 57 v8plus_async_callback(uv_async_t *async, __attribute__((unused)) int status) 58 { 59 if (v8plus_in_event_thread() != B_TRUE) 60 v8plus_panic("async callback called outside of event loop"); 61 62 for (;;) { 63 v8plus_async_call_t *vac = NULL; 64 65 /* 66 * Fetch the next queued method: 67 */ 68 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0) 69 v8plus_panic("could not lock async queue mutex"); 70 if (!STAILQ_EMPTY(&_v8plus_callq)) { 71 vac = STAILQ_FIRST(&_v8plus_callq); 72 STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry); 73 } 74 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0) 75 v8plus_panic("could not unlock async queue mutex"); 76 77 if (vac == NULL) 78 break; 79 80 /* 81 * Run the queued method: 82 */ 83 if (pthread_mutex_lock(&vac->vac_mtx) != 0) 84 v8plus_panic("could not lock async call mutex"); 85 86 if (vac->vac_run == B_TRUE) 87 v8plus_panic("async call already run"); 88 89 vac->vac_return = v8plus_method_call_direct(vac->vac_cop, 90 vac->vac_name, vac->vac_lp); 91 vac->vac_run = B_TRUE; 92 93 if (pthread_cond_broadcast(&vac->vac_cv) != 0) 94 v8plus_panic("could not signal async call condvar"); 95 if (pthread_mutex_unlock(&vac->vac_mtx) != 0) 96 v8plus_panic("could not unlock async call mutex"); 97 } 98 } 99 100 nvlist_t * 101 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp) 102 { 103 v8plus_async_call_t vac; 104 105 if (v8plus_in_event_thread() == B_TRUE) { 106 /* 107 * We're running in the event loop thread, so we can make the 108 * call directly. 109 */ 110 return (v8plus_method_call_direct(cop, name, lp)); 111 } 112 113 /* 114 * As we cannot manipulate v8plus/V8/Node structures directly from 115 * outside the event loop thread, we push the call arguments onto a 116 * queue and post to the event loop thread. We then sleep on our 117 * condition variable until the event loop thread makes the call 118 * for us and wakes us up. 119 */ 120 vac.vac_cop = cop; 121 vac.vac_name = name; 122 vac.vac_lp = lp; 123 if (pthread_mutex_init(&vac.vac_mtx, NULL) != 0) 124 v8plus_panic("could not init async call mutex"); 125 if (pthread_cond_init(&vac.vac_cv, NULL) != 0) 126 v8plus_panic("could not init async call condvar"); 127 vac.vac_run = B_FALSE; 128 vac.vac_return = NULL; 129 130 /* 131 * Post request to queue: 132 */ 133 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0) 134 v8plus_panic("could not lock async queue mutex"); 135 STAILQ_INSERT_TAIL(&_v8plus_callq, &vac, vac_callq_entry); 136 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0) 137 v8plus_panic("could not unlock async queue mutex"); 138 uv_async_send(&_v8plus_uv_async); 139 140 /* 141 * Wait for our request to be serviced on the Event Loop thread: 142 */ 143 if (pthread_mutex_lock(&vac.vac_mtx) != 0) 144 v8plus_panic("could not lock async call mutex"); 145 while (vac.vac_run == B_FALSE) { 146 if (pthread_cond_wait(&vac.vac_cv, &vac.vac_mtx) != 0) 147 v8plus_panic("could not wait on async call condvar"); 148 } 149 150 return (vac.vac_return); 151 } 152 153 154 /* 155 * Initialise structures for off-event-loop method calls. 156 * 157 * Note that uv_async_init() must be called inside the libuv Event Loop, so we 158 * do it here. We also want to record the thread ID of the Event Loop thread 159 * so as to determine what kind of method calls to make later. 160 */ 161 void 162 v8plus_crossthread_init(void) 163 { 164 _v8plus_uv_event_thread = pthread_self(); 165 if (uv_async_init(uv_default_loop(), &_v8plus_uv_async, 166 v8plus_async_callback) != 0) 167 v8plus_panic("unable to initialise uv_async_t"); 168 if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0) 169 v8plus_panic("unable to initialise mutex"); 170 } 171 172 nvlist_t * 173 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap) 174 { 175 if (fmt == NULL) { 176 if (e == V8PLUSERR_NOERROR) { 177 *_v8plus_errmsg = '\0'; 178 } else { 179 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, 180 "%s", v8plus_strerror(e)); 181 } 182 } else { 183 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap); 184 } 185 _v8plus_errno = e; 186 187 return (NULL); 188 } 189 190 nvlist_t * 191 v8plus_error(v8plus_errno_t e, const char *fmt, ...) 192 { 193 va_list ap; 194 195 va_start(ap, fmt); 196 (void) v8plus_verror(e, fmt, ap); 197 va_end(ap); 198 199 return (NULL); 200 } 201 202 static void __NORETURN 203 v8plus_vpanic(const char *fmt, va_list ap) 204 { 205 (void) vfprintf(stderr, fmt, ap); 206 (void) fflush(stderr); 207 abort(); 208 } 209 210 void 211 v8plus_panic(const char *fmt, ...) 212 { 213 va_list ap; 214 215 va_start(ap, fmt); 216 v8plus_vpanic(fmt, ap); 217 va_end(ap); 218 } 219 220 nvlist_t * 221 v8plus_nverr(int nverr, const char *member) 222 { 223 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, 224 "nvlist manipulation error on member %s: %s", 225 member == NULL ? "<none>" : member, strerror(nverr)); 226 227 switch (nverr) { 228 case ENOMEM: 229 _v8plus_errno = V8PLUSERR_NOMEM; 230 break; 231 case EINVAL: 232 _v8plus_errno = V8PLUSERR_YOUSUCK; 233 break; 234 default: 235 _v8plus_errno = V8PLUSERR_UNKNOWN; 236 break; 237 } 238 239 return (NULL); 240 } 241 242 nvlist_t * 243 v8plus_syserr(int syserr, const char *fmt, ...) 244 { 245 v8plus_errno_t e; 246 va_list ap; 247 248 switch (syserr) { 249 case ENOMEM: 250 e = V8PLUSERR_NOMEM; 251 break; 252 case EBADF: 253 e = V8PLUSERR_BADF; 254 break; 255 default: 256 e = V8PLUSERR_UNKNOWN; 257 break; 258 } 259 260 va_start(ap, fmt); 261 (void) v8plus_verror(e, fmt, ap); 262 va_end(ap); 263 264 return (NULL); 265 } 266 267 /* 268 * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void. 269 */ 270 nvlist_t * 271 v8plus_void(void) 272 { 273 return (v8plus_error(V8PLUSERR_NOERROR, NULL)); 274 } 275 276 v8plus_type_t 277 v8plus_typeof(const nvpair_t *pp) 278 { 279 data_type_t t = nvpair_type((nvpair_t *)pp); 280 281 switch (t) { 282 case DATA_TYPE_DOUBLE: 283 return (V8PLUS_TYPE_NUMBER); 284 case DATA_TYPE_STRING: 285 return (V8PLUS_TYPE_STRING); 286 case DATA_TYPE_NVLIST: 287 return (V8PLUS_TYPE_OBJECT); 288 case DATA_TYPE_BOOLEAN_VALUE: 289 return (V8PLUS_TYPE_BOOLEAN); 290 case DATA_TYPE_BOOLEAN: 291 return (V8PLUS_TYPE_UNDEFINED); 292 case DATA_TYPE_BYTE: 293 { 294 uchar_t v; 295 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0) 296 return (V8PLUS_TYPE_INVALID); 297 return (V8PLUS_TYPE_NULL); 298 } 299 case DATA_TYPE_UINT64_ARRAY: 300 { 301 uint64_t *vp; 302 uint_t nv; 303 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 || 304 nv != 1) { 305 return (V8PLUS_TYPE_INVALID); 306 } 307 return (V8PLUS_TYPE_JSFUNC); 308 } 309 default: 310 return (V8PLUS_TYPE_INVALID); 311 } 312 } 313 314 static int 315 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp) 316 { 317 data_type_t dt = nvpair_type((nvpair_t *)pp); 318 319 switch (t) { 320 case V8PLUS_TYPE_NONE: 321 return (-1); 322 case V8PLUS_TYPE_STRING: 323 if (dt == DATA_TYPE_STRING) { 324 if (vp != NULL) { 325 (void) nvpair_value_string((nvpair_t *)pp, 326 (char **)vp); 327 } 328 return (0); 329 } 330 return (-1); 331 case V8PLUS_TYPE_NUMBER: 332 if (dt == DATA_TYPE_DOUBLE) { 333 if (vp != NULL) { 334 (void) nvpair_value_double((nvpair_t *)pp, 335 (double *)vp); 336 } 337 return (0); 338 } 339 return (-1); 340 case V8PLUS_TYPE_BOOLEAN: 341 if (dt == DATA_TYPE_BOOLEAN_VALUE) { 342 if (vp != NULL) { 343 (void) nvpair_value_boolean_value( 344 (nvpair_t *)pp, (boolean_t *)vp); 345 } 346 return (0); 347 } 348 return (-1); 349 case V8PLUS_TYPE_JSFUNC: 350 if (dt == DATA_TYPE_UINT64_ARRAY) { 351 uint_t nv; 352 uint64_t *vpp; 353 354 if (nvpair_value_uint64_array((nvpair_t *)pp, 355 &vpp, &nv) == 0 && nv == 1) { 356 if (vp != NULL) 357 *(v8plus_jsfunc_t *)vp = vpp[0]; 358 return (0); 359 } 360 } 361 return (-1); 362 case V8PLUS_TYPE_OBJECT: 363 if (dt == DATA_TYPE_NVLIST) { 364 if (vp != NULL) { 365 (void) nvpair_value_nvlist((nvpair_t *)pp, 366 (nvlist_t **)vp); 367 } 368 return (0); 369 } 370 return (-1); 371 case V8PLUS_TYPE_NULL: 372 if (dt == DATA_TYPE_BYTE) { 373 uchar_t v; 374 375 if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 && 376 v == 0) 377 return (0); 378 } 379 return (-1); 380 case V8PLUS_TYPE_UNDEFINED: 381 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1); 382 case V8PLUS_TYPE_ANY: 383 if (vp != NULL) 384 *(const nvpair_t **)vp = pp; 385 return (0); 386 case V8PLUS_TYPE_INVALID: 387 if (vp != NULL) 388 *(data_type_t *)vp = dt; 389 return (0); 390 case V8PLUS_TYPE_STRNUMBER64: 391 if (dt == DATA_TYPE_STRING) { 392 char *s; 393 uint64_t v; 394 395 (void) nvpair_value_string((nvpair_t *)pp, &s); 396 errno = 0; 397 v = (uint64_t)strtoull(s, NULL, 0); 398 if (errno != 0) 399 return (-1); 400 if (vp != NULL) 401 *(uint64_t *)vp = v; 402 return (0); 403 } 404 return (-1); 405 default: 406 return (-1); 407 } 408 } 409 410 int 411 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...) 412 { 413 v8plus_type_t nt; 414 nvpair_t *pp; 415 void *vp; 416 va_list ap; 417 uint_t i; 418 char buf[32]; 419 420 va_start(ap, t); 421 422 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) { 423 switch (nt) { 424 case V8PLUS_TYPE_UNDEFINED: 425 case V8PLUS_TYPE_NULL: 426 break; 427 default: 428 (void) va_arg(ap, void *); 429 } 430 431 (void) snprintf(buf, sizeof (buf), "%u", i); 432 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) { 433 (void) v8plus_error(V8PLUSERR_MISSINGARG, 434 "argument %u is required", i); 435 return (-1); 436 } 437 438 if (v8plus_arg_value(nt, pp, NULL) != 0) { 439 (void) v8plus_error(V8PLUSERR_BADARG, 440 "argument %u is of incorrect type", i); 441 return (-1); 442 } 443 444 nt = va_arg(ap, data_type_t); 445 } 446 447 va_end(ap); 448 449 if (flags & V8PLUS_ARG_F_NOEXTRA) { 450 (void) snprintf(buf, sizeof (buf), "%u", i); 451 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) { 452 (void) v8plus_error(V8PLUSERR_EXTRAARG, 453 "superfluous extra argument(s) detected"); 454 return (-1); 455 } 456 } 457 458 va_start(ap, t); 459 460 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) { 461 switch (nt) { 462 case V8PLUS_TYPE_UNDEFINED: 463 case V8PLUS_TYPE_NULL: 464 vp = NULL; 465 break; 466 default: 467 vp = va_arg(ap, void *); 468 } 469 470 (void) snprintf(buf, sizeof (buf), "%u", i); 471 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0); 472 VERIFY(v8plus_arg_value(nt, pp, vp) == 0); 473 474 nt = va_arg(ap, data_type_t); 475 } 476 477 va_end(ap); 478 479 return (0); 480 } 481 482 static int 483 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap) 484 { 485 v8plus_type_t nt = t; 486 char *name; 487 int err; 488 489 /* 490 * Do not call va_start() or va_end() in this function! We are limited 491 * to a single traversal of the arguments so that we can recurse to 492 * handle embedded object definitions. 493 */ 494 495 while (nt != V8PLUS_TYPE_NONE) { 496 name = va_arg(*ap, char *); 497 498 switch (nt) { 499 case V8PLUS_TYPE_STRING: 500 { 501 char *s = va_arg(*ap, char *); 502 if ((err = nvlist_add_string(lp, name, s)) != 0) { 503 (void) v8plus_nverr(err, name); 504 return (-1); 505 } 506 break; 507 } 508 case V8PLUS_TYPE_NUMBER: 509 { 510 double d = va_arg(*ap, double); 511 if ((err = nvlist_add_double(lp, name, d)) != 0) { 512 (void) v8plus_nverr(err, name); 513 return (-1); 514 } 515 break; 516 } 517 case V8PLUS_TYPE_BOOLEAN: 518 { 519 boolean_t b = va_arg(*ap, boolean_t); 520 if ((err = nvlist_add_boolean_value(lp, 521 name, b)) != 0) { 522 (void) v8plus_nverr(err, name); 523 return (-1); 524 } 525 break; 526 } 527 case V8PLUS_TYPE_JSFUNC: 528 { 529 v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t); 530 if ((err = nvlist_add_uint64_array(lp, 531 name, &j, 1)) != 0) { 532 (void) v8plus_nverr(err, name); 533 return (-1); 534 } 535 if ((err = nvlist_add_string_array(lp, 536 V8PLUS_JSF_COOKIE, NULL, 0)) != 0) { 537 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE); 538 return (-1); 539 } 540 v8plus_jsfunc_hold(j); 541 break; 542 } 543 case V8PLUS_TYPE_OBJECT: 544 { 545 const nvlist_t *op = va_arg(*ap, const nvlist_t *); 546 if ((err = nvlist_add_nvlist(lp, name, 547 (nvlist_t *)op)) != 0) { 548 (void) v8plus_nverr(err, name); 549 return (-1); 550 } 551 break; 552 } 553 case V8PLUS_TYPE_NULL: 554 if ((err = nvlist_add_byte(lp, name, 0)) != 0) { 555 (void) v8plus_nverr(err, name); 556 return (-1); 557 } 558 break; 559 case V8PLUS_TYPE_UNDEFINED: 560 if ((err = nvlist_add_boolean(lp, name)) != 0) { 561 (void) v8plus_nverr(err, name); 562 return (-1); 563 } 564 break; 565 case V8PLUS_TYPE_ANY: 566 { 567 nvpair_t *pp = va_arg(*ap, nvpair_t *); 568 if ((err = nvlist_add_nvpair(lp, pp)) != 0) { 569 (void) v8plus_nverr(err, name); 570 return (-1); 571 } 572 break; 573 } 574 case V8PLUS_TYPE_STRNUMBER64: 575 { 576 uint64_t v = va_arg(*ap, uint64_t); 577 char s[32]; 578 (void) snprintf(s, sizeof (s), "%" PRIu64, v); 579 if ((err = nvlist_add_string(lp, name, s)) != 0) { 580 (void) v8plus_nverr(err, name); 581 return (-1); 582 } 583 break; 584 } 585 case V8PLUS_TYPE_INL_OBJECT: 586 { 587 nvlist_t *slp; 588 589 nt = va_arg(*ap, v8plus_type_t); 590 err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0); 591 if (err != 0) { 592 (void) v8plus_nverr(err, name); 593 return (-1); 594 } 595 if (v8plus_obj_vsetprops(slp, nt, ap) != 0) 596 return (-1); 597 598 err = nvlist_add_nvlist(lp, name, slp); 599 nvlist_free(slp); 600 if (err != 0) { 601 (void) v8plus_nverr(err, name); 602 return (-1); 603 } 604 break; 605 } 606 case V8PLUS_TYPE_INVALID: 607 default: 608 (void) v8plus_error(V8PLUSERR_YOUSUCK, 609 "invalid property type %d", nt); 610 return (-1); 611 } 612 613 nt = va_arg(*ap, v8plus_type_t); 614 } 615 616 return (0); 617 } 618 619 nvlist_t * 620 v8plus_obj(v8plus_type_t t, ...) 621 { 622 nvlist_t *rp; 623 va_list ap; 624 int err; 625 626 if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0) 627 return (v8plus_nverr(err, NULL)); 628 629 va_start(ap, t); 630 err = v8plus_obj_vsetprops(rp, t, &ap); 631 va_end(ap); 632 633 if (err != 0) { 634 nvlist_free(rp); 635 rp = NULL; 636 } 637 638 return (rp); 639 } 640 641 int 642 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...) 643 { 644 va_list ap; 645 int err; 646 647 va_start(ap, t); 648 err = v8plus_obj_vsetprops(lp, t, &ap); 649 va_end(ap); 650 651 return (err); 652 } 653 654 static void 655 v8plus_uv_worker(uv_work_t *wp) 656 { 657 v8plus_uv_ctx_t *cp = wp->data; 658 659 cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx); 660 } 661 662 static void 663 v8plus_uv_completion(uv_work_t *wp) 664 { 665 v8plus_uv_ctx_t *cp = wp->data; 666 667 cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result); 668 v8plus_obj_rele(cp->vuc_obj); 669 free(cp); 670 free(wp); 671 } 672 673 void 674 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker, 675 v8plus_completion_f completion) 676 { 677 uv_work_t *wp = malloc(sizeof (uv_work_t)); 678 v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t)); 679 680 bzero(wp, sizeof (uv_work_t)); 681 bzero(cp, sizeof (v8plus_uv_ctx_t)); 682 683 v8plus_obj_hold(cop); 684 cp->vuc_obj = cop; 685 cp->vuc_ctx = ctxp; 686 cp->vuc_worker = worker; 687 cp->vuc_completion = completion; 688 wp->data = cp; 689 690 uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker, 691 v8plus_uv_completion); 692 }