1 /* 2 * Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * # OpenWrt uloop event loop 19 * 20 * The `uloop` binding provides functions for integrating with the OpenWrt 21 * {@link https://github.com/openwrt/libubox/blob/master/uloop.h uloop library}. 22 * 23 * Functions can be individually imported and directly accessed using the 24 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 25 * syntax: 26 * 27 * ```javascript 28 * import { init, handle, timer, interval, process, signal, task, run } from 'uloop'; 29 * 30 * init(); 31 * 32 * handle(…); 33 * timer(…); 34 * interval(…); 35 * process(…); 36 * signal(…); 37 * task(…); 38 * 39 * run(); 40 * ``` 41 * 42 * Alternatively, the module namespace can be imported using a wildcard import 43 * statement: 44 * 45 * ```javascript 46 * import * as uloop from 'uloop'; 47 * 48 * uloop.init(); 49 * 50 * uloop.handle(…); 51 * uloop.timer(…); 52 * uloop.interval(…); 53 * uloop.process(…); 54 * uloop.signal(…); 55 * uloop.task(…); 56 * 57 * uloop.run(); 58 * ``` 59 * 60 * Additionally, the uloop binding namespace may also be imported by invoking 61 * the `ucode` interpreter with the `-luloop` switch. 62 * 63 * @module uloop 64 */ 65 66 #include <errno.h> 67 #include <string.h> 68 #include <unistd.h> 69 #include <limits.h> 70 #include <fcntl.h> 71 72 #include <libubox/uloop.h> 73 74 #include "ucode/module.h" 75 #include "ucode/platform.h" 76 77 #define ok_return(expr) do { last_error = 0; return (expr); } while(0) 78 #define err_return(err) do { last_error = err; return NULL; } while(0) 79 80 static int last_error = 0; 81 82 typedef struct { 83 uc_vm_t *vm; 84 uc_value_t *obj; 85 } uc_uloop_cb_t; 86 87 static void * 88 uc_uloop_alloc(uc_vm_t *vm, const char *type, size_t size, uc_value_t *func) 89 { 90 uc_uloop_cb_t *cb; 91 uc_value_t *obj; 92 93 obj = ucv_resource_create_ex(vm, type, (void **)&cb, 2, size); 94 if (!obj) 95 return NULL; 96 97 cb->vm = vm; 98 cb->obj = ucv_get(obj); 99 ucv_resource_persistent_set(obj, true); 100 ucv_resource_value_set(obj, 0, ucv_get(func)); 101 102 return cb; 103 } 104 105 static void 106 uc_uloop_cb_free(uc_uloop_cb_t *cb) 107 { 108 uc_value_t *obj = cb->obj; 109 110 if (!obj) 111 return; 112 113 cb->obj = NULL; 114 115 ucv_resource_persistent_set(obj, false); 116 ucv_put(obj); 117 } 118 119 static bool 120 uc_uloop_vm_call(uc_vm_t *vm, bool mcall, size_t nargs) 121 { 122 uc_value_t *exh, *val; 123 124 if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE) 125 return true; 126 127 exh = uc_vm_registry_get(vm, "uloop.ex_handler"); 128 if (!ucv_is_callable(exh)) 129 goto error; 130 131 val = uc_vm_exception_object(vm); 132 uc_vm_stack_push(vm, ucv_get(exh)); 133 uc_vm_stack_push(vm, val); 134 135 if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE) 136 goto error; 137 138 ucv_put(uc_vm_stack_pop(vm)); 139 140 return false; 141 142 error: 143 uloop_end(); 144 return false; 145 } 146 147 static void 148 uc_uloop_cb_invoke(uc_uloop_cb_t *cb, uc_value_t *arg) 149 { 150 uc_vm_t *vm = cb->vm; 151 uc_value_t *func = ucv_resource_value_get(cb->obj, 0); 152 153 if (!ucv_is_callable(func)) 154 return; 155 156 uc_vm_stack_push(vm, ucv_get(cb->obj)); 157 uc_vm_stack_push(vm, ucv_get(func)); 158 uc_vm_stack_push(vm, ucv_get(arg)); 159 160 if (uc_uloop_vm_call(vm, true, 1)) 161 ucv_put(uc_vm_stack_pop(vm)); 162 } 163 164 /** 165 * Retrieves the last error message. 166 * 167 * This function retrieves the last error message generated by the uloop event loop. 168 * If no error occurred, it returns `null`. 169 * 170 * @function module:uloop#error 171 * 172 * @returns {?string} 173 * Returns the last error message as a string, or `null` if no error occurred. 174 * 175 * @example 176 * // Retrieve the last error message 177 * const errorMessage = uloop.error(); 178 * 179 * if (errorMessage) 180 * printf(`Error message: ${errorMessage}\n`); 181 * else 182 * printf("No error occurred\n"); 183 */ 184 static uc_value_t * 185 uc_uloop_error(uc_vm_t *vm, size_t nargs) 186 { 187 uc_value_t *errmsg; 188 189 if (last_error == 0) 190 return NULL; 191 192 errmsg = ucv_string_new(strerror(last_error)); 193 last_error = 0; 194 195 return errmsg; 196 } 197 198 /** 199 * Initializes the uloop event loop. 200 * 201 * This function initializes the uloop event loop, allowing subsequent 202 * usage of uloop functionalities. It takes no arguments. 203 * 204 * Returns `true` on success. 205 * Returns `null` if an error occurred during initialization. 206 * 207 * @function module:uloop#init 208 * 209 * @returns {?boolean} 210 * Returns `true` on success, `null` on error. 211 * 212 * @example 213 * // Initialize the uloop event loop 214 * const success = uloop.init(); 215 * 216 * if (success) 217 * printf("uloop event loop initialized successfully\n"); 218 * else 219 * die(`Initialization failure: ${uloop.error()}\n`); 220 */ 221 static uc_value_t * 222 uc_uloop_init(uc_vm_t *vm, size_t nargs) 223 { 224 int rv = uloop_init(); 225 226 if (rv == -1) 227 err_return(errno); 228 229 ok_return(ucv_boolean_new(true)); 230 } 231 232 /** 233 * Runs the uloop event loop. 234 * 235 * This function starts running the uloop event loop, allowing it to handle 236 * scheduled events and callbacks. If a timeout value is provided and is 237 * non-negative, the event loop will run for that amount of milliseconds 238 * before returning. If the timeout is omitted or negative, the event loop 239 * runs indefinitely until explicitly stopped. 240 * 241 * @function module:uloop#run 242 * 243 * @param {number} [timeout=-1] 244 * Optional. The timeout value in milliseconds for running the event loop. 245 * Defaults to -1, indicating an indefinite run. 246 * 247 * @returns {?boolean} 248 * Returns `true` on success, `null` on error. 249 * 250 * @example 251 * // Run the uloop event loop indefinitely 252 * const success = uloop.run(); 253 * if (success) 254 * printf("uloop event loop ran successfully\n"); 255 * else 256 * die(`Error occurred during uloop execution: ${uloop.error()}\n`); 257 * 258 * // Run the uloop event loop for 1000 milliseconds 259 * const success = uloop.run(1000); 260 * if (success) 261 * printf("uloop event loop ran successfully\n"); 262 * else 263 * die(`Error occurred during uloop execution: ${uloop.error()}\n`); 264 */ 265 static uc_value_t * 266 uc_uloop_run(uc_vm_t *vm, size_t nargs) 267 { 268 uc_value_t *timeout = uc_fn_arg(0); 269 int t, rv; 270 271 errno = 0; 272 t = timeout ? (int)ucv_int64_get(timeout) : -1; 273 274 if (errno) 275 err_return(errno); 276 277 rv = uloop_run_timeout(t); 278 279 ok_return(ucv_int64_new(rv)); 280 } 281 282 /** 283 * Checks if the uloop event loop is currently shutting down. 284 * 285 * This function checks whether the uloop event loop is currently in the process 286 * of shutting down. 287 * 288 * @function module:uloop#cancelling 289 * 290 * @returns {boolean} 291 * Returns `true` if uloop is currently shutting down, `false` otherwise. 292 * 293 * @example 294 * // Check if the uloop event loop is shutting down 295 * const shuttingDown = uloop.cancelling(); 296 * if (shuttingDown) 297 * printf("uloop event loop is currently shutting down\n"); 298 * else 299 * printf("uloop event loop is not shutting down\n"); 300 */ 301 static uc_value_t * 302 uc_uloop_cancelling(uc_vm_t *vm, size_t nargs) 303 { 304 ok_return(ucv_boolean_new(uloop_cancelling())); 305 } 306 307 /** 308 * Checks if the uloop event loop is currently running. 309 * 310 * This function checks whether the uloop event loop is currently started 311 * and running. 312 * 313 * @function module:uloop#running 314 * 315 * @returns {boolean} 316 * Returns `true` if the event loop is currently running, `false` otherwise. 317 * 318 * @example 319 * // Check if the uloop event loop is running 320 * const isRunning = uloop.running(); 321 * if (isRunning) 322 * printf("uloop event loop is currently running\n"); 323 * else 324 * printf("uloop event loop is not running\n"); 325 */ 326 static uc_value_t * 327 uc_uloop_running(uc_vm_t *vm, size_t nargs) 328 { 329 bool prev = uloop_cancelled; 330 bool active; 331 332 uloop_cancelled = true; 333 active = uloop_cancelling(); 334 uloop_cancelled = prev; 335 336 ok_return(ucv_boolean_new(active)); 337 } 338 339 /** 340 * Halts the uloop event loop. 341 * 342 * This function halts the uloop event loop, stopping its execution and 343 * preventing further processing of scheduled events and callbacks. 344 * 345 * Expired timeouts and already queued event callbacks are still run to 346 * completion. 347 * 348 * @function module:uloop#end 349 * 350 * @returns {void} 351 * This function does not return any value. 352 * 353 * @example 354 * // Halt the uloop event loop 355 * uloop.end(); 356 */ 357 static uc_value_t * 358 uc_uloop_end(uc_vm_t *vm, size_t nargs) 359 { 360 uloop_end(); 361 362 ok_return(NULL); 363 } 364 365 /** 366 * Stops the uloop event loop and cancels pending timeouts and events. 367 * 368 * This function immediately stops the uloop event loop, cancels all pending 369 * timeouts and events, unregisters all handles, and deallocates associated 370 * resources. 371 * 372 * @function module:uloop#done 373 * 374 * @returns {void} 375 * This function does not return any value. 376 * 377 * @example 378 * // Stop the uloop event loop and clean up resources 379 * uloop.done(); 380 */ 381 static uc_value_t * 382 uc_uloop_done(uc_vm_t *vm, size_t nargs) 383 { 384 uloop_done(); 385 386 ok_return(NULL); 387 } 388 389 390 /** 391 * Represents a uloop timer instance as returned by 392 * {@link module:uloop#timer|timer()}. 393 * 394 * @class module:uloop.timer 395 * @hideconstructor 396 * 397 * @see {@link module:uloop#timer|timer()} 398 * 399 * @example 400 * 401 * const timeout = uloop.timer(…); 402 * 403 * timeout.set(…); 404 * timeout.remaining(); 405 * timeout.cancel(); 406 */ 407 typedef struct { 408 uc_uloop_cb_t cb; 409 struct uloop_timeout timeout; 410 } uc_uloop_timer_t; 411 412 static int 413 uc_uloop_timeout_clear(uc_uloop_timer_t *timer) 414 { 415 int rv = uloop_timeout_cancel(&timer->timeout); 416 417 uc_uloop_cb_free(&timer->cb); 418 419 return rv; 420 } 421 422 /** 423 * Rearms the uloop timer with the specified timeout. 424 * 425 * This method rearms the uloop timer with the specified timeout value, 426 * allowing it to trigger after the specified amount of time. If no timeout 427 * value is provided or if the provided value is negative, the timer remains 428 * disabled until rearmed with a positive timeout value. 429 * 430 * @function module:uloop.timer#set 431 * 432 * @param {number} [timeout=-1] 433 * Optional. The timeout value in milliseconds until the timer expires. 434 * Defaults to -1, which disables the timer until rearmed with a positive timeout. 435 * 436 * @returns {?boolean} 437 * Returns `true` on success, `null` on error, such as an invalid timeout argument. 438 * 439 * @example 440 * const timeout = uloop.timer(…); 441 * 442 * // Rearm the uloop timer with a timeout of 1000 milliseconds 443 * timeout.set(1000); 444 * 445 * // Disable the uloop timer 446 * timeout.set(); 447 */ 448 static uc_value_t * 449 uc_uloop_timer_set(uc_vm_t *vm, size_t nargs) 450 { 451 uc_uloop_timer_t *timer = uc_fn_thisval("uloop.timer"); 452 uc_value_t *timeout = uc_fn_arg(0); 453 int t, rv; 454 455 if (!timer) 456 err_return(EINVAL); 457 458 errno = 0; 459 t = timeout ? (int)ucv_int64_get(timeout) : -1; 460 461 if (errno) 462 err_return(errno); 463 464 rv = uloop_timeout_set(&timer->timeout, t); 465 466 ok_return(ucv_boolean_new(rv == 0)); 467 } 468 469 /** 470 * Returns the number of milliseconds until the uloop timer expires. 471 * 472 * This method returns the remaining time until the uloop timer expires. If 473 * the timer is not armed (i.e., disabled), it returns -1. 474 * 475 * @function module:uloop.timer#remaining 476 * 477 * @returns {number} 478 * The number of milliseconds until the timer expires, or -1 if the timer is not armed. 479 * 480 * @example 481 * // Get the remaining time until the uloop timer expires (~500ms) 482 * const remainingTime = timer.remaining(); 483 * if (remainingTime !== -1) 484 * printf("Time remaining until timer expires: %d ms\n", remainingTime); 485 * else 486 * printf("Timer is not armed\n"); 487 */ 488 static uc_value_t * 489 uc_uloop_timer_remaining(uc_vm_t *vm, size_t nargs) 490 { 491 uc_uloop_timer_t *timer = uc_fn_thisval("uloop.timer"); 492 int64_t rem; 493 494 if (!timer) 495 err_return(EINVAL); 496 497 #ifdef HAVE_ULOOP_TIMEOUT_REMAINING64 498 rem = uloop_timeout_remaining64(&timer->timeout); 499 #else 500 rem = (int64_t)uloop_timeout_remaining(&timer->timeout); 501 #endif 502 503 ok_return(ucv_int64_new(rem)); 504 } 505 506 /** 507 * Cancels the uloop timer, disarming it and removing it from the event loop. 508 * 509 * This method destroys the uloop timer and releases its associated resources. 510 * 511 * @function module:uloop.timer#cancel 512 * 513 * @returns {boolean} 514 * Returns `true` on success. 515 * 516 * @example 517 * // Cancel the uloop timer 518 * timer.cancel(); 519 */ 520 static uc_value_t * 521 uc_uloop_timer_cancel(uc_vm_t *vm, size_t nargs) 522 { 523 uc_uloop_timer_t *timer = uc_fn_thisval("uloop.timer"); 524 int rv; 525 526 if (!timer) 527 err_return(EINVAL); 528 529 rv = uc_uloop_timeout_clear(timer); 530 531 ok_return(ucv_boolean_new(rv == 0)); 532 } 533 534 static void 535 uc_uloop_timer_cb(struct uloop_timeout *timeout) 536 { 537 uc_uloop_timer_t *timer = container_of(timeout, uc_uloop_timer_t, timeout); 538 539 uc_uloop_cb_invoke(&timer->cb, NULL); 540 } 541 542 /** 543 * Creates a timer instance for scheduling callbacks. 544 * 545 * This function creates a timer instance for scheduling callbacks to be 546 * executed after a specified timeout duration. It takes an optional timeout 547 * parameter, which defaults to -1, indicating that the timer is initially not 548 * armed and can be enabled later by invoking the `.set(timeout)` method on the 549 * instance. 550 * 551 * A callback function must be provided to be executed when the timer expires. 552 * 553 * @function module:uloop#timer 554 * 555 * @param {number} [timeout=-1] 556 * Optional. The timeout duration in milliseconds. Defaults to -1, indicating 557 * the timer is not initially armed. 558 * 559 * @param {Function} callback 560 * The callback function to be executed when the timer expires. 561 * 562 * @returns {?module:uloop.timer} 563 * Returns a timer instance for scheduling callbacks. 564 * Returns `null` when the timeout or callback arguments are invalid. 565 * 566 * @example 567 * // Create a timer with a callback to be executed after 1000 milliseconds 568 * const myTimer = uloop.timer(1000, () => { 569 * printf("Timer expired!\n"); 570 * }); 571 * 572 * // Later enable the timer with a timeout of 500 milliseconds 573 * myTimer.set(500); 574 */ 575 static uc_value_t * 576 uc_uloop_timer(uc_vm_t *vm, size_t nargs) 577 { 578 uc_value_t *timeout = uc_fn_arg(0); 579 uc_value_t *callback = uc_fn_arg(1); 580 uc_uloop_timer_t *timer; 581 int t; 582 583 errno = 0; 584 t = timeout ? ucv_int64_get(timeout) : -1; 585 586 if (errno) 587 err_return(errno); 588 589 if (!ucv_is_callable(callback)) 590 err_return(EINVAL); 591 592 timer = uc_uloop_alloc(vm, "uloop.timer", sizeof(*timer), callback); 593 timer->timeout.cb = uc_uloop_timer_cb; 594 595 if (t >= 0) 596 uloop_timeout_set(&timer->timeout, t); 597 598 ok_return(timer->cb.obj); 599 } 600 601 602 /** 603 * Represents a uloop handle instance as returned by 604 * {@link module:uloop#handle|handle()}. 605 * 606 * @class module:uloop.handle 607 * @hideconstructor 608 * 609 * @see {@link module:uloop#handle|handle()} 610 * 611 * @example 612 * 613 * const handle = uloop.handle(…); 614 * 615 * handle.fileno(); 616 * handle.handle(); 617 * 618 * handle.delete(); 619 */ 620 typedef struct { 621 uc_uloop_cb_t cb; 622 struct uloop_fd fd; 623 } uc_uloop_handle_t; 624 625 static int 626 uc_uloop_handle_clear(uc_uloop_handle_t *handle) 627 { 628 int rv = uloop_fd_delete(&handle->fd); 629 630 uc_uloop_cb_free(&handle->cb); 631 632 return rv; 633 } 634 635 /** 636 * Returns the file descriptor number. 637 * 638 * This method returns the file descriptor number associated with the underlying 639 * handle, which might refer to a socket or file instance. 640 * 641 * @function module:uloop.handle#fileno 642 * 643 * @returns {number} 644 * The file descriptor number associated with the handle. 645 * 646 * @example 647 * // Get the file descriptor number associated with the uloop handle 648 * const fd = handle.fileno(); 649 * printf("File descriptor number: %d\n", fd); 650 */ 651 static uc_value_t * 652 uc_uloop_handle_fileno(uc_vm_t *vm, size_t nargs) 653 { 654 uc_uloop_handle_t *handle = uc_fn_thisval("uloop.handle"); 655 656 if (!handle) 657 err_return(EINVAL); 658 659 ok_return(ucv_int64_new(handle->fd.fd)); 660 } 661 662 /** 663 * Returns the underlying file or socket instance. 664 * 665 * This method returns the underlying file or socket instance associated with 666 * the uloop handle. 667 * 668 * @function module:uloop.handle#handle 669 * 670 * @returns {module:fs.file|module:fs.proc|module:socket.socket} 671 * The underlying file or socket instance associated with the handle. 672 * 673 * @example 674 * // Get the associated file or socket instance 675 * const fileOrSocket = handle.handle(); 676 * printf("Handle: %s\n", fileOrSocket); // e.g. <socket 0x5> or <fs.proc …> 677 */ 678 static uc_value_t * 679 uc_uloop_handle_handle(uc_vm_t *vm, size_t nargs) 680 { 681 uc_uloop_handle_t *handle = uc_fn_thisval("uloop.handle"); 682 683 if (!handle) 684 err_return(EINVAL); 685 686 ok_return(ucv_get(ucv_resource_value_get(handle->cb.obj, 1))); 687 } 688 689 /** 690 * Unregisters the uloop handle. 691 * 692 * This method unregisters the uloop handle from the uloop event loop and frees 693 * any associated resources. After calling this method, the handle instance 694 * should no longer be used. 695 * 696 * @function module:uloop.handle#delete 697 * 698 * @returns {void} 699 * This function does not return a value. 700 * 701 * @example 702 * // Unregister the uloop handle and free associated resources 703 * handle.delete(); 704 * printf("Handle deleted successfully\n"); 705 */ 706 static uc_value_t * 707 uc_uloop_handle_delete(uc_vm_t *vm, size_t nargs) 708 { 709 uc_uloop_handle_t *handle = uc_fn_thisval("uloop.handle"); 710 int rv; 711 712 if (!handle) 713 err_return(EINVAL); 714 715 rv = uc_uloop_handle_clear(handle); 716 717 if (rv != 0) 718 err_return(errno); 719 720 ok_return(ucv_boolean_new(true)); 721 } 722 723 static void 724 uc_uloop_handle_cb(struct uloop_fd *fd, unsigned int flags) 725 { 726 uc_uloop_handle_t *handle = container_of(fd, uc_uloop_handle_t, fd); 727 uc_value_t *f = ucv_uint64_new(flags); 728 729 uc_uloop_cb_invoke(&handle->cb, f); 730 ucv_put(f); 731 } 732 733 static int 734 get_fd(uc_vm_t *vm, uc_value_t *val) 735 { 736 uc_value_t *fn; 737 int64_t n; 738 int fd; 739 740 fn = ucv_property_get(val, "fileno"); 741 742 if (ucv_is_callable(fn)) { 743 uc_vm_stack_push(vm, ucv_get(val)); 744 uc_vm_stack_push(vm, ucv_get(fn)); 745 746 if (uc_vm_call(vm, true, 0) == EXCEPTION_NONE) { 747 val = uc_vm_stack_pop(vm); 748 } 749 else { 750 errno = EBADF; 751 val = NULL; 752 } 753 } 754 else { 755 ucv_get(val); 756 } 757 758 n = ucv_int64_get(val); 759 760 if (errno) { 761 fd = -1; 762 } 763 else if (n < 0 || n > (int64_t)INT_MAX) { 764 errno = EBADF; 765 fd = -1; 766 } 767 else { 768 fd = (int)n; 769 } 770 771 ucv_put(val); 772 773 return fd; 774 } 775 776 /** 777 * Creates a handle instance for monitoring file descriptor events. 778 * 779 * This function creates a handle instance for monitoring events on a file 780 * descriptor, file, or socket. It takes the file or socket handle, a callback 781 * function to be invoked when the specified IO events occur, and bitwise OR-ed 782 * flags of IO events (`ULOOP_READ`, `ULOOP_WRITE`) that the callback should be 783 * invoked for. 784 * 785 * @function module:uloop#handle 786 * 787 * @param {number|module:fs.file|module:fs.proc|module:socket.socket} handle 788 * The file handle (descriptor number, file or socket instance). 789 * 790 * @param {Function} callback 791 * The callback function to be invoked when the specified IO events occur. 792 * 793 * @param {number} events 794 * Bitwise OR-ed flags of IO events (`ULOOP_READ`, `ULOOP_WRITE`) that the 795 * callback should be invoked for. 796 * 797 * @returns {?module:uloop.handle} 798 * Returns a handle instance for monitoring file descriptor events. 799 * Returns `null` when the handle, callback or signal arguments are invalid. 800 * 801 * @example 802 * // Create a handle for monitoring read events on file descriptor 3 803 * const myHandle = uloop.handle(3, (events) => { 804 * if (events & ULOOP_READ) 805 * printf("Read event occurred!\n"); 806 * }, uloop.ULOOP_READ); 807 * 808 * // Check socket for writability 809 * const sock = socket.connect("example.org", 80); 810 * uloop.handle(sock, (events) => { 811 * sock.send("GET / HTTP/1.0\r\n\r\n"); 812 * }, uloop.ULOOP_WRITE) 813 */ 814 static uc_value_t * 815 uc_uloop_handle(uc_vm_t *vm, size_t nargs) 816 { 817 uc_value_t *fileno = uc_fn_arg(0); 818 uc_value_t *callback = uc_fn_arg(1); 819 uc_value_t *flags = uc_fn_arg(2); 820 uc_uloop_handle_t *handle; 821 int fd, ret; 822 uint64_t f; 823 824 fd = get_fd(vm, fileno); 825 826 if (fd == -1) 827 err_return(errno); 828 829 f = ucv_uint64_get(flags); 830 831 if (errno) 832 err_return(errno); 833 834 if (f == 0 || f > (uint64_t)UINT_MAX) 835 err_return(EINVAL); 836 837 if (!ucv_is_callable(callback)) 838 err_return(EINVAL); 839 840 handle = uc_uloop_alloc(vm, "uloop.handle", sizeof(*handle), callback); 841 handle->fd.fd = fd; 842 handle->fd.cb = uc_uloop_handle_cb; 843 844 ret = uloop_fd_add(&handle->fd, (unsigned int)f); 845 if (ret != 0) { 846 ucv_put(handle->cb.obj); 847 err_return(errno); 848 } 849 850 ucv_resource_value_set(handle->cb.obj, 1, ucv_get(fileno)); 851 ok_return(handle->cb.obj); 852 } 853 854 855 /** 856 * Represents a uloop process instance as returned by 857 * {@link module:uloop#process|process()}. 858 * 859 * @class module:uloop.process 860 * @hideconstructor 861 * 862 * @see {@link module:uloop#process|process()} 863 * 864 * @example 865 * 866 * const proc = uloop.process(…); 867 * 868 * proc.pid(); 869 * 870 * proc.delete(); 871 */ 872 typedef struct { 873 uc_uloop_cb_t cb; 874 struct uloop_process process; 875 } uc_uloop_process_t; 876 877 static int 878 uc_uloop_process_clear(uc_uloop_process_t *process) 879 { 880 int rv = uloop_process_delete(&process->process); 881 882 uc_uloop_cb_free(&process->cb); 883 884 return rv; 885 } 886 887 /** 888 * Returns the process ID. 889 * 890 * This method returns the process ID (PID) of the operating system process 891 * launched by {@link module:uloop#process|process(). 892 * 893 * @function module:uloop.process#pid 894 * 895 * @returns {number} 896 * The process ID (PID) of the associated launched process. 897 * 898 * @example 899 * const proc = uloop.process(…); 900 * 901 * printf("Process ID: %d\n", proc.pid()); 902 */ 903 static uc_value_t * 904 uc_uloop_process_pid(uc_vm_t *vm, size_t nargs) 905 { 906 uc_uloop_process_t *process = uc_fn_thisval("uloop.process"); 907 908 if (!process) 909 err_return(EINVAL); 910 911 ok_return(ucv_int64_new(process->process.pid)); 912 } 913 914 /** 915 * Unregisters the process from uloop. 916 * 917 * This method unregisters the process from the uloop event loop and releases 918 * any associated resources. However, note that the operating system process 919 * itself is not terminated by this method. 920 * 921 * @function module:uloop.process#delete 922 * 923 * @returns {boolean} 924 * Returns `true` on success. 925 * 926 * @example 927 * const proc = uloop.process(…); 928 * 929 * proc.delete(); 930 */ 931 static uc_value_t * 932 uc_uloop_process_delete(uc_vm_t *vm, size_t nargs) 933 { 934 uc_uloop_process_t *process = uc_fn_thisval("uloop.process"); 935 int rv; 936 937 if (!process) 938 err_return(EINVAL); 939 940 rv = uc_uloop_process_clear(process); 941 942 if (rv != 0) 943 err_return(EINVAL); 944 945 ok_return(ucv_boolean_new(true)); 946 } 947 948 static void 949 uc_uloop_process_cb(struct uloop_process *proc, int exitcode) 950 { 951 uc_uloop_process_t *process = container_of(proc, uc_uloop_process_t, process); 952 uc_value_t *e = ucv_int64_new(exitcode >> 8); 953 954 uc_uloop_cb_invoke(&process->cb, e); 955 uc_uloop_process_clear(process); 956 ucv_put(e); 957 } 958 959 /** 960 * Creates a process instance for executing external programs. 961 * 962 * This function creates a process instance for executing external programs. 963 * It takes the executable path string, an optional string array as the argument 964 * vector, an optional dictionary describing environment variables, and a 965 * callback function to be invoked when the invoked process ends. 966 * 967 * @function module:uloop#process 968 * 969 * @param {string} executable 970 * The path to the executable program. 971 * 972 * @param {string[]} [args] 973 * Optional. An array of strings representing the arguments passed to the 974 * executable. 975 * 976 * @param {Object<string, *>} [env] 977 * Optional. A dictionary describing environment variables for the process. 978 * 979 * @param {Function} callback 980 * The callback function to be invoked when the invoked process ends. 981 * 982 * @returns {?module:uloop.process} 983 * Returns a process instance for executing external programs. 984 * Returns `null` on error, e.g. due to `exec()` failure or invalid arguments. 985 * 986 * @example 987 * // Create a process instance for executing 'ls' command 988 * const myProcess = uloop.process("/bin/ls", ["-l", "/tmp"], null, (code) => { 989 * printf(`Process exited with code ${code}\n`); 990 * }); 991 */ 992 static uc_value_t * 993 uc_uloop_process(uc_vm_t *vm, size_t nargs) 994 { 995 uc_value_t *executable = uc_fn_arg(0); 996 uc_value_t *arguments = uc_fn_arg(1); 997 uc_value_t *env_arg = uc_fn_arg(2); 998 uc_value_t *callback = uc_fn_arg(3); 999 uc_uloop_process_t *process; 1000 uc_stringbuf_t *buf; 1001 char **argp, **envp; 1002 pid_t pid; 1003 size_t i; 1004 1005 if (ucv_type(executable) != UC_STRING || 1006 (arguments && ucv_type(arguments) != UC_ARRAY) || 1007 (env_arg && ucv_type(env_arg) != UC_OBJECT) || 1008 !ucv_is_callable(callback)) { 1009 err_return(EINVAL); 1010 } 1011 1012 pid = fork(); 1013 1014 if (pid == -1) 1015 err_return(errno); 1016 1017 if (pid == 0) { 1018 argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *)); 1019 envp = calloc(ucv_object_length(env_arg) + 1, sizeof(char *)); 1020 1021 if (!argp || !envp) 1022 _exit(-1); 1023 1024 argp[0] = ucv_to_string(vm, executable); 1025 1026 for (i = 0; i < ucv_array_length(arguments); i++) 1027 argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i)); 1028 1029 i = 0; 1030 1031 ucv_object_foreach(env_arg, envk, envv) { 1032 buf = xprintbuf_new(); 1033 1034 ucv_stringbuf_printf(buf, "%s=", envk); 1035 ucv_to_stringbuf(vm, buf, envv, false); 1036 1037 envp[i++] = buf->buf; 1038 1039 free(buf); 1040 } 1041 1042 execvpe((const char *)ucv_string_get(executable), 1043 (char * const *)argp, (char * const *)envp); 1044 1045 _exit(-1); 1046 } 1047 1048 process = uc_uloop_alloc(vm, "uloop.process", sizeof(*process), callback); 1049 process->process.pid = pid; 1050 process->process.cb = uc_uloop_process_cb; 1051 uloop_process_add(&process->process); 1052 1053 ok_return(process->cb.obj); 1054 } 1055 1056 1057 static bool 1058 readall(int fd, void *buf, size_t len) 1059 { 1060 ssize_t rlen; 1061 1062 while (len > 0) { 1063 rlen = read(fd, buf, len); 1064 1065 if (rlen == -1) { 1066 if (errno == EINTR) 1067 continue; 1068 1069 return false; 1070 } 1071 1072 if (rlen == 0) { 1073 errno = EINTR; 1074 1075 return false; 1076 } 1077 1078 buf += rlen; 1079 len -= rlen; 1080 } 1081 1082 return true; 1083 } 1084 1085 static bool 1086 writeall(int fd, void *buf, size_t len) 1087 { 1088 ssize_t wlen; 1089 1090 while (len > 0) { 1091 wlen = write(fd, buf, len); 1092 1093 if (wlen == -1) { 1094 if (errno == EINTR) 1095 continue; 1096 1097 return false; 1098 } 1099 1100 buf += wlen; 1101 len -= wlen; 1102 } 1103 1104 return true; 1105 } 1106 1107 1108 /** 1109 * Represents a uloop task communication pipe instance, passed as sole argument 1110 * to the task function by {@link module:uloop#task|task()}. 1111 * 1112 * @class module:uloop.pipe 1113 * @hideconstructor 1114 * 1115 * @see {@link module:uloop#task|task()} 1116 * 1117 * @example * 1118 * const task = uloop.task((pipe) => { 1119 * … 1120 * pipe.send(); 1121 * … 1122 * pipe.receive(); 1123 * … 1124 * }, …); 1125 */ 1126 typedef struct { 1127 int input; 1128 int output; 1129 bool has_sender; 1130 bool has_receiver; 1131 } uc_uloop_pipe_t; 1132 1133 static uc_value_t * 1134 uc_uloop_pipe_send_common(uc_vm_t *vm, uc_value_t *msg, int fd) 1135 { 1136 uc_stringbuf_t *buf; 1137 size_t len; 1138 bool rv; 1139 1140 buf = xprintbuf_new(); 1141 1142 printbuf_memset(buf, 0, 0, sizeof(len)); 1143 ucv_to_stringbuf(vm, buf, msg, true); 1144 1145 len = printbuf_length(buf); 1146 memcpy(buf->buf, &len, sizeof(len)); 1147 1148 rv = writeall(fd, buf->buf, len); 1149 1150 printbuf_free(buf); 1151 1152 if (!rv) 1153 err_return(errno); 1154 1155 ok_return(ucv_boolean_new(true)); 1156 } 1157 1158 /** 1159 * Sends a serialized message to the task handle. 1160 * 1161 * This method serializes the provided message and sends it over the task 1162 * communication pipe. In the main thread, the message is deserialized and 1163 * passed as an argument to the output callback function registered with the 1164 * task handle. 1165 * 1166 * @function module:uloop.pipe#send 1167 * 1168 * @param {*} msg 1169 * The message to be serialized and sent over the pipe. It can be of arbitrary type. 1170 * 1171 * @returns {?boolean} 1172 * Returns `true` on success, indicating that the message was successfully sent 1173 * over the pipe. Returns `null` on error, such as when there's no output 1174 * callback registered with the task handle. 1175 * 1176 * @example 1177 * // Send a message over the uloop pipe 1178 * const success = pipe.send(message); 1179 * 1180 * if (success) 1181 * printf("Message sent successfully\n"); 1182 * else 1183 * die(`Error sending message: ${uloop.error()}\n`); 1184 */ 1185 static uc_value_t * 1186 uc_uloop_pipe_send(uc_vm_t *vm, size_t nargs) 1187 { 1188 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1189 uc_value_t *msg = uc_fn_arg(0); 1190 1191 if (!pipe) 1192 err_return(EINVAL); 1193 1194 if (!pipe->has_receiver) 1195 err_return(EPIPE); 1196 1197 ok_return(uc_uloop_pipe_send_common(vm, msg, pipe->output)); 1198 } 1199 1200 static bool 1201 uc_uloop_pipe_receive_common(uc_vm_t *vm, int fd, uc_value_t **res, bool skip) 1202 { 1203 enum json_tokener_error err = json_tokener_error_parse_eof; 1204 json_tokener *tok = NULL; 1205 json_object *jso = NULL; 1206 char buf[1024]; 1207 ssize_t rlen; 1208 size_t len; 1209 1210 *res = NULL; 1211 1212 if (!readall(fd, &len, sizeof(len))) 1213 err_return(errno); 1214 1215 /* message length 0 is special, means input requested on other pipe */ 1216 if (len == 0) 1217 err_return(ENODATA); 1218 1219 /* valid messages should be at least sizeof(len) plus one byte of payload */ 1220 if (len <= sizeof(len)) 1221 err_return(EINVAL); 1222 1223 len -= sizeof(len); 1224 1225 while (len > 0) { 1226 rlen = read(fd, buf, len < sizeof(buf) ? len : sizeof(buf)); 1227 1228 if (rlen == -1) { 1229 if (errno == EINTR) 1230 continue; 1231 1232 goto read_fail; 1233 } 1234 1235 /* premature EOF */ 1236 if (rlen == 0) { 1237 errno = EPIPE; 1238 goto read_fail; 1239 } 1240 1241 if (!skip) { 1242 if (!tok) 1243 tok = xjs_new_tokener(); 1244 1245 jso = json_tokener_parse_ex(tok, buf, rlen); 1246 err = json_tokener_get_error(tok); 1247 } 1248 1249 len -= rlen; 1250 } 1251 1252 if (!skip) { 1253 if (err == json_tokener_continue) { 1254 jso = json_tokener_parse_ex(tok, "\0", 1); 1255 err = json_tokener_get_error(tok); 1256 } 1257 1258 json_tokener_free(tok); 1259 1260 if (err != json_tokener_success) { 1261 errno = EINVAL; 1262 goto read_fail; 1263 } 1264 1265 *res = ucv_from_json(vm, jso); 1266 1267 json_object_put(jso); 1268 } 1269 1270 return true; 1271 1272 read_fail: 1273 if (tok) 1274 json_tokener_free(tok); 1275 1276 json_object_put(jso); 1277 err_return(errno); 1278 } 1279 1280 /** 1281 * Reads input from the task handle. 1282 * 1283 * This method reads input from the task communication pipe. The input callback 1284 * function registered with the task handle is invoked to return the input data, 1285 * which is then serialized, sent over the pipe, and deserialized by the receive 1286 * method. 1287 * 1288 * @function module:uloop.pipe#receive 1289 * 1290 * @returns {?*} 1291 * Returns the deserialized message read from the task communication pipe. 1292 * Returns `null` on error, such as when there's no input callback registered 1293 * on the task handle. 1294 * 1295 * @example 1296 * // Read input from the task communication pipe 1297 * const message = pipe.receive(); 1298 * 1299 * if (message !== null) 1300 * printf("Received message: %s\n", message); 1301 * else 1302 * die(`Error receiving message: ${uloop.error()}\n`); 1303 */ 1304 static uc_value_t * 1305 uc_uloop_pipe_receive(uc_vm_t *vm, size_t nargs) 1306 { 1307 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1308 uc_value_t *rv; 1309 size_t len = 0; 1310 1311 if (!pipe) 1312 err_return(EINVAL); 1313 1314 if (!pipe->has_sender) 1315 err_return(EPIPE); 1316 1317 /* send zero-length message to signal input request */ 1318 writeall(pipe->output, &len, sizeof(len)); 1319 1320 /* receive input message */ 1321 uc_uloop_pipe_receive_common(vm, pipe->input, &rv, false); 1322 1323 return rv; 1324 } 1325 1326 /** 1327 * Checks if the task handle provides input. 1328 * 1329 * This method checks if the task handle has an input callback registered. 1330 * It returns a boolean value indicating whether an input callback is present. 1331 * 1332 * @function module:uloop.pipe#sending 1333 * 1334 * @returns {boolean} 1335 * Returns `true` if the remote task handle has an input callback 1336 * registered, otherwise returns `false`. 1337 * 1338 * @example 1339 * // Check if the remote task handle has an input callback 1340 * const hasInputCallback = pipe.sending(); 1341 * 1342 * if (hasInputCallback) 1343 * printf("Input callback is registered on task handle\n"); 1344 * else 1345 * printf("No input callback on the task handle\n"); 1346 */ 1347 static uc_value_t * 1348 uc_uloop_pipe_sending(uc_vm_t *vm, size_t nargs) 1349 { 1350 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1351 1352 if (!pipe) 1353 err_return(EINVAL); 1354 1355 ok_return(ucv_boolean_new(pipe->has_sender)); 1356 } 1357 1358 /** 1359 * Checks if the task handle reads output. 1360 * 1361 * This method checks if the task handle has an output callback registered. 1362 * It returns a boolean value indicating whether an output callback is present. 1363 * 1364 * @function module:uloop.pipe#receiving 1365 * 1366 * @returns {boolean} 1367 * Returns `true` if the task handle has an output callback registered, 1368 * otherwise returns `false`. 1369 * 1370 * @example 1371 * // Check if the task handle has an output callback 1372 * const hasOutputCallback = pipe.receiving(); 1373 * 1374 * if (hasOutputCallback) 1375 * printf("Output callback is registered on task handle\n"); 1376 * else 1377 * printf("No output callback on the task handle\n"); 1378 */ 1379 static uc_value_t * 1380 uc_uloop_pipe_receiving(uc_vm_t *vm, size_t nargs) 1381 { 1382 uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe"); 1383 1384 if (!pipe) 1385 err_return(EINVAL); 1386 1387 ok_return(ucv_boolean_new(pipe->has_receiver)); 1388 } 1389 1390 1391 /** 1392 * Represents a uloop task instance as returned by 1393 * {@link module:uloop#task|task()}. 1394 * 1395 * @class module:uloop.task 1396 * @hideconstructor 1397 * 1398 * @see {@link module:uloop#task|task()} 1399 * 1400 * @example 1401 * 1402 * const task = uloop.task(…); 1403 * 1404 * task.pid(); 1405 * task.finished(); 1406 * 1407 * task.kill(); 1408 */ 1409 typedef struct { 1410 uc_uloop_cb_t cb; 1411 struct uloop_process process; 1412 struct uloop_fd output; 1413 bool finished; 1414 int input_fd; 1415 uc_value_t *input_cb; 1416 uc_value_t *output_cb; 1417 } uc_uloop_task_t; 1418 1419 static int 1420 patch_devnull(int fd, bool write) 1421 { 1422 int devnull = open("/dev/null", write ? O_WRONLY : O_RDONLY); 1423 1424 if (devnull != -1) { 1425 dup2(fd, devnull); 1426 close(fd); 1427 } 1428 1429 return devnull; 1430 } 1431 1432 static void 1433 uloop_fd_close(struct uloop_fd *fd) { 1434 if (fd->fd == -1) 1435 return; 1436 1437 close(fd->fd); 1438 fd->fd = -1; 1439 } 1440 1441 static void 1442 uc_uloop_task_clear(uc_uloop_task_t *task) 1443 { 1444 if (task->input_fd >= 0) { 1445 close(task->input_fd); 1446 task->input_fd = -1; 1447 1448 uloop_fd_close(&task->output); 1449 uloop_process_delete(&task->process); 1450 } 1451 1452 uc_uloop_cb_free(&task->cb); 1453 } 1454 1455 /** 1456 * Returns the process ID. 1457 * 1458 * This method returns the process ID (PID) of the underlying forked process 1459 * launched by {@link module:uloop#task|task(). 1460 * 1461 * @function module:uloop.task#pid 1462 * 1463 * @returns {number} 1464 * The process ID (PID) of the forked task process. 1465 * 1466 * @example 1467 * const task = uloop.task(…); 1468 * 1469 * printf("Process ID: %d\n", task.pid()); 1470 */ 1471 static uc_value_t * 1472 uc_uloop_task_pid(uc_vm_t *vm, size_t nargs) 1473 { 1474 uc_uloop_task_t *task = uc_fn_thisval("uloop.task"); 1475 1476 if (!task) 1477 err_return(EINVAL); 1478 1479 if (task->finished) 1480 err_return(ESRCH); 1481 1482 ok_return(ucv_int64_new(task->process.pid)); 1483 } 1484 1485 /** 1486 * Terminates the task process. 1487 * 1488 * This method terminates the task process. It sends a termination signal to 1489 * the task process, causing it to exit. Returns `true` on success, indicating 1490 * that the task process was successfully terminated. Returns `null` on error, 1491 * such as when the task process has already terminated. 1492 * 1493 * @function module:uloop.task#kill 1494 * 1495 * @returns {?boolean} 1496 * Returns `true` when the task process was successfully terminated. 1497 * Returns `null` on error, such as when the process has already terminated. 1498 * 1499 * @example 1500 * // Terminate the task process 1501 * const success = task.kill(); 1502 * 1503 * if (success) 1504 * printf("Task process terminated successfully\n"); 1505 * else 1506 * die(`Error terminating task process: ${uloop.error()}\n`); 1507 */ 1508 static uc_value_t * 1509 uc_uloop_task_kill(uc_vm_t *vm, size_t nargs) 1510 { 1511 uc_uloop_task_t *task = uc_fn_thisval("uloop.task"); 1512 int rv; 1513 1514 if (!task) 1515 err_return(EINVAL); 1516 1517 if (task->finished) 1518 err_return(ESRCH); 1519 1520 rv = kill(task->process.pid, SIGTERM); 1521 1522 if (rv == -1) 1523 err_return(errno); 1524 1525 ok_return(ucv_boolean_new(true)); 1526 } 1527 1528 /** 1529 * Checks if the task ran to completion. 1530 * 1531 * This method checks if the task function has already run to completion. 1532 * It returns a boolean value indicating whether the task function has finished 1533 * executing. 1534 * 1535 * @function module:uloop.task#finished 1536 * 1537 * @returns {boolean} 1538 * Returns `true` if the task function has already run to completion, otherwise 1539 * returns `false`. 1540 * 1541 * @example 1542 * // Check if the task function has finished executing 1543 * const isFinished = task.finished(); 1544 * 1545 * if (isFinished) 1546 * printf("Task function has finished executing\n"); 1547 * else 1548 * printf("Task function is still running\n"); 1549 */ 1550 static uc_value_t * 1551 uc_uloop_task_finished(uc_vm_t *vm, size_t nargs) 1552 { 1553 uc_uloop_task_t *task = uc_fn_thisval("uloop.task"); 1554 1555 if (!task) 1556 err_return(EINVAL); 1557 1558 ok_return(ucv_boolean_new(task->finished)); 1559 } 1560 1561 static void 1562 uc_uloop_task_output_cb(struct uloop_fd *fd, unsigned int flags) 1563 { 1564 uc_uloop_task_t *task = container_of(fd, uc_uloop_task_t, output); 1565 uc_value_t *obj = task->cb.obj; 1566 uc_vm_t *vm = task->cb.vm; 1567 uc_value_t *msg = NULL; 1568 1569 if (flags & ULOOP_READ) { 1570 while (true) { 1571 if (!uc_uloop_pipe_receive_common(vm, fd->fd, &msg, !task->output_cb)) { 1572 /* input requested */ 1573 if (last_error == ENODATA) { 1574 uc_vm_stack_push(vm, ucv_get(obj)); 1575 uc_vm_stack_push(vm, ucv_get(task->input_cb)); 1576 1577 if (!uc_uloop_vm_call(vm, true, 0)) 1578 return; 1579 1580 msg = uc_vm_stack_pop(vm); 1581 uc_uloop_pipe_send_common(vm, msg, task->input_fd); 1582 ucv_put(msg); 1583 1584 continue; 1585 } 1586 1587 /* error */ 1588 break; 1589 } 1590 1591 if (task->output_cb) { 1592 uc_vm_stack_push(vm, ucv_get(obj)); 1593 uc_vm_stack_push(vm, ucv_get(task->output_cb)); 1594 uc_vm_stack_push(vm, msg); 1595 1596 if (!uc_uloop_vm_call(vm, true, 1)) 1597 return; 1598 1599 ucv_put(uc_vm_stack_pop(vm)); 1600 } 1601 else { 1602 ucv_put(msg); 1603 } 1604 } 1605 } 1606 1607 if (!fd->registered && task->finished) 1608 uc_uloop_task_clear(task); 1609 } 1610 1611 static void 1612 uc_uloop_task_process_cb(struct uloop_process *proc, int exitcode) 1613 { 1614 uc_uloop_task_t *task = container_of(proc, uc_uloop_task_t, process); 1615 1616 task->finished = true; 1617 1618 uc_uloop_task_output_cb(&task->output, ULOOP_READ); 1619 } 1620 1621 /** 1622 * Creates a task instance for executing background tasks. 1623 * 1624 * This function creates a task instance for executing background tasks. 1625 * It takes the task function to be invoked as a background process, 1626 * an optional output callback function to be invoked when output is received 1627 * from the task, and an optional input callback function to be invoked 1628 * when input is required by the task. 1629 * 1630 * @function module:uloop#task 1631 * 1632 * @param {Function} taskFunction 1633 * The task function to be invoked as a background process. 1634 * 1635 * @param {Function} [outputCallback] 1636 * Optional. The output callback function to be invoked when output is received 1637 * from the task. It is invoked with the output data as the argument. 1638 * 1639 * @param {Function} [inputCallback] 1640 * Optional. The input callback function to be invoked when input is required 1641 * by the task. It is invoked with a function to send input to the task 1642 * as the argument. 1643 * 1644 * @returns {?module:uloop.task} 1645 * Returns a task instance for executing background tasks. 1646 * Returns `null` on error, e.g. due to fork failure or invalid arguments. 1647 * 1648 * @example 1649 * // Create a task instance for executing a background task 1650 * const myTask = uloop.task( 1651 * (pipe) => { 1652 * // Task logic 1653 * pipe.send("Hello from the task\n"); 1654 * const input = pipe.receive(); 1655 * printf(`Received input from main thread: ${input}\n`); 1656 * }, 1657 * (output) => { 1658 * // Output callback, invoked when task function calls pipe.send() 1659 * printf(`Received output from task: ${output}\n`); 1660 * }, 1661 * () => { 1662 * // Input callback, invoked when task function calls pipe.receive() 1663 * return "Input from main thread\n"; 1664 * } 1665 * ); 1666 */ 1667 static uc_value_t * 1668 uc_uloop_task(uc_vm_t *vm, size_t nargs) 1669 { 1670 uc_value_t *func = uc_fn_arg(0); 1671 uc_value_t *output_cb = uc_fn_arg(1); 1672 uc_value_t *input_cb = uc_fn_arg(2); 1673 int outpipe[2] = { -1, -1 }; 1674 int inpipe[2] = { -1, -1 }; 1675 uc_value_t *res, *cbs, *p; 1676 uc_uloop_pipe_t *tpipe; 1677 uc_uloop_task_t *task; 1678 pid_t pid; 1679 int err; 1680 1681 if (!ucv_is_callable(func) || 1682 (output_cb && !ucv_is_callable(output_cb)) || 1683 (input_cb && !ucv_is_callable(input_cb))) 1684 err_return(EINVAL); 1685 1686 if (pipe(outpipe) == -1 || pipe(inpipe) == -1) { 1687 err = errno; 1688 1689 close(outpipe[0]); close(outpipe[1]); 1690 close(inpipe[0]); close(inpipe[1]); 1691 1692 err_return(err); 1693 } 1694 1695 pid = fork(); 1696 1697 if (pid == -1) 1698 err_return(errno); 1699 1700 if (pid == 0) { 1701 uloop_done(); 1702 1703 patch_devnull(0, false); 1704 patch_devnull(1, true); 1705 patch_devnull(2, true); 1706 1707 vm->output = fdopen(1, "w"); 1708 1709 close(inpipe[1]); 1710 close(outpipe[0]); 1711 1712 tpipe = xalloc(sizeof(*tpipe)); 1713 tpipe->input = inpipe[0]; 1714 tpipe->output = outpipe[1]; 1715 tpipe->has_sender = input_cb; 1716 tpipe->has_receiver = output_cb; 1717 1718 p = ucv_resource_create(vm, "uloop.pipe", tpipe); 1719 1720 uc_vm_stack_push(vm, func); 1721 uc_vm_stack_push(vm, ucv_get(p)); 1722 1723 if (uc_uloop_vm_call(vm, false, 1)) { 1724 res = uc_vm_stack_pop(vm); 1725 uc_uloop_pipe_send_common(vm, res, tpipe->output); 1726 ucv_put(res); 1727 } 1728 1729 ucv_put(p); 1730 1731 _exit(0); 1732 } 1733 1734 close(inpipe[0]); 1735 close(outpipe[1]); 1736 1737 task = uc_uloop_alloc(vm, "uloop.task", sizeof(*task), func); 1738 task->process.pid = pid; 1739 task->process.cb = uc_uloop_task_process_cb; 1740 1741 task->output.fd = outpipe[0]; 1742 task->output.cb = uc_uloop_task_output_cb; 1743 task->output_cb = output_cb; 1744 uloop_fd_add(&task->output, ULOOP_READ); 1745 1746 if (input_cb) { 1747 task->input_fd = inpipe[1]; 1748 task->input_cb = input_cb; 1749 } 1750 else { 1751 task->input_fd = -1; 1752 close(inpipe[1]); 1753 } 1754 1755 uloop_process_add(&task->process); 1756 1757 cbs = ucv_array_new(NULL); 1758 ucv_array_set(cbs, 0, ucv_get(output_cb)); 1759 ucv_array_set(cbs, 1, ucv_get(input_cb)); 1760 ucv_resource_value_set(task->cb.obj, 1, ucv_get(cbs)); 1761 1762 ok_return(task->cb.obj); 1763 } 1764 1765 1766 /** 1767 * Represents a uloop interval timer instance as returned by 1768 * {@link module:uloop#interval|interval()}. 1769 * 1770 * @class module:uloop.interval 1771 * @hideconstructor 1772 * 1773 * @see {@link module:uloop#interval|interval()} 1774 * 1775 * @example 1776 * 1777 * const intv = uloop.interval(…); 1778 * 1779 * intv.set(…); 1780 * intv.remaining(); 1781 * intv.expirations(); 1782 * intv.cancel(); 1783 */ 1784 #ifdef HAVE_ULOOP_INTERVAL 1785 typedef struct { 1786 uc_uloop_cb_t cb; 1787 struct uloop_interval interval; 1788 } uc_uloop_interval_t; 1789 1790 static int 1791 uc_uloop_interval_clear(uc_uloop_interval_t *interval) 1792 { 1793 int rv = uloop_interval_cancel(&interval->interval); 1794 1795 uc_uloop_cb_free(&interval->cb); 1796 1797 return rv; 1798 } 1799 1800 /** 1801 * Rearms the uloop interval timer with the specified interval. 1802 * 1803 * This method rearms the interval timer with the specified interval value, 1804 * allowing it to trigger repeatedly after the specified amount of time. If no 1805 * interval value is provided or if the provided value is negative, the interval 1806 * remains disabled until rearmed with a positive interval value. 1807 * 1808 * @function module:uloop.interval#set 1809 * 1810 * @param {number} [interval=-1] 1811 * Optional. The interval value in milliseconds specifying when the interval 1812 * triggers again. Defaults to -1, which disables the interval until rearmed 1813 * with a positive interval value. 1814 * 1815 * @returns {?boolean} 1816 * Returns `true` on success, `null` on error, such as an invalid interval argument. 1817 * 1818 * @example 1819 * // Rearm the uloop interval with a interval of 1000 milliseconds 1820 * const success = interval.set(1000); 1821 * 1822 * if (success) 1823 * printf("Interval rearmed successfully\n"); 1824 * else 1825 * printf("Error occurred while rearming interval: ${uloop.error()}\n"); 1826 * 1827 * // Disable the uloop interval 1828 * const success = interval.set(); 1829 * 1830 * if (success) 1831 * printf("Interval disabled successfully\n"); 1832 * else 1833 * printf("Error occurred while disabling interval: ${uloop.error()}\n"); 1834 */ 1835 static uc_value_t * 1836 uc_uloop_interval_set(uc_vm_t *vm, size_t nargs) 1837 { 1838 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1839 uc_value_t *timeout = uc_fn_arg(0); 1840 int t, rv; 1841 1842 if (!interval) 1843 err_return(EINVAL); 1844 1845 errno = 0; 1846 t = timeout ? (int)ucv_int64_get(timeout) : -1; 1847 1848 if (errno) 1849 err_return(errno); 1850 1851 rv = uloop_interval_set(&interval->interval, t); 1852 1853 ok_return(ucv_boolean_new(rv == 0)); 1854 } 1855 1856 /** 1857 * Returns the milliseconds until the next expiration. 1858 * 1859 * This method returns the remaining time until the uloop interval expires 1860 * and triggers again. If the interval is not armed (i.e., disabled), 1861 * it returns -1. 1862 * 1863 * @function module:uloop.interval#remaining 1864 * 1865 * @returns {number} 1866 * The milliseconds until the next expiration of the uloop interval, or -1 if 1867 * the interval is not armed. 1868 * 1869 * @example 1870 * // Get the milliseconds until the next expiration of the uloop interval 1871 * const remainingTime = interval.remaining(); 1872 * 1873 * if (remainingTime !== -1) 1874 * printf("Milliseconds until next expiration: %d\n", remainingTime); 1875 * else 1876 * printf("Interval is not armed\n"); 1877 */ 1878 static uc_value_t * 1879 uc_uloop_interval_remaining(uc_vm_t *vm, size_t nargs) 1880 { 1881 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1882 1883 if (!interval) 1884 err_return(EINVAL); 1885 1886 ok_return(ucv_int64_new(uloop_interval_remaining(&interval->interval))); 1887 } 1888 1889 /** 1890 * Returns number of times the interval timer fired. 1891 * 1892 * This method returns the number of times the uloop interval timer has expired 1893 * (fired) since it was instantiated. 1894 * 1895 * @function module:uloop.interval#expirations 1896 * 1897 * @returns {number} 1898 * The number of times the uloop interval timer has expired (fired). 1899 * 1900 * @example 1901 * // Get the number of times the uloop interval timer has expired 1902 * const expirations = interval.expirations(); 1903 * printf("Number of expirations: %d\n", expirations); 1904 */ 1905 static uc_value_t * 1906 uc_uloop_interval_expirations(uc_vm_t *vm, size_t nargs) 1907 { 1908 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1909 1910 if (!interval) 1911 err_return(EINVAL); 1912 1913 ok_return(ucv_int64_new(interval->interval.expirations)); 1914 } 1915 1916 /** 1917 * Cancels the uloop interval. 1918 * 1919 * This method cancels the uloop interval, disarming it and removing it from the 1920 * event loop. Associated resources are released. 1921 * 1922 * @function module:uloop.interval#cancel 1923 * 1924 * @returns {boolean} 1925 * Returns `true` on success. 1926 * 1927 * @example 1928 * // Cancel the uloop interval 1929 * interval.cancel(); 1930 */ 1931 static uc_value_t * 1932 uc_uloop_interval_cancel(uc_vm_t *vm, size_t nargs) 1933 { 1934 uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval"); 1935 int rv; 1936 1937 if (!interval) 1938 err_return(EINVAL); 1939 1940 rv = uc_uloop_interval_clear(interval); 1941 1942 ok_return(ucv_boolean_new(rv == 0)); 1943 } 1944 1945 static void 1946 uc_uloop_interval_cb(struct uloop_interval *uintv) 1947 { 1948 uc_uloop_interval_t *interval = container_of(uintv, uc_uloop_interval_t, interval); 1949 1950 uc_uloop_cb_invoke(&interval->cb, NULL); 1951 } 1952 1953 /** 1954 * Creates an interval instance for scheduling repeated callbacks. 1955 * 1956 * This function creates an interval instance for scheduling repeated callbacks 1957 * to be executed at regular intervals. It takes an optional timeout parameter, 1958 * which defaults to -1, indicating that the interval is initially not armed 1959 * and can be armed later with the `.set(timeout)` method. A callback function 1960 * must be provided to be executed when the interval expires. 1961 * 1962 * @function module:uloop#interval 1963 * 1964 * @param {number} [timeout=-1] 1965 * Optional. The interval duration in milliseconds. Defaults to -1, indicating 1966 * the interval is not initially armed. 1967 * 1968 * @param {Function} callback 1969 * The callback function to be executed when the interval expires. 1970 * 1971 * @returns {?module:uloop.interval} 1972 * Returns an interval instance for scheduling repeated callbacks. 1973 * Returns `null` when the timeout or callback arguments are invalid. 1974 * 1975 * @example 1976 * // Create an interval with a callback to be executed every 1000 milliseconds 1977 * const myInterval = uloop.interval(1000, () => { 1978 * printf("Interval callback executed!\n"); 1979 * }); 1980 * 1981 * // Later arm the interval to start executing the callback every 500 milliseconds 1982 * myInterval.set(500); 1983 */ 1984 static uc_value_t * 1985 uc_uloop_interval(uc_vm_t *vm, size_t nargs) 1986 { 1987 uc_value_t *timeout = uc_fn_arg(0); 1988 uc_value_t *callback = uc_fn_arg(1); 1989 uc_uloop_interval_t *interval; 1990 int t; 1991 1992 errno = 0; 1993 t = timeout ? ucv_int64_get(timeout) : -1; 1994 1995 if (errno) 1996 err_return(errno); 1997 1998 if (!ucv_is_callable(callback)) 1999 err_return(EINVAL); 2000 2001 interval = uc_uloop_alloc(vm, "uloop.interval", sizeof(*interval), callback); 2002 interval->interval.cb = uc_uloop_interval_cb; 2003 if (t >= 0) 2004 uloop_interval_set(&interval->interval, t); 2005 2006 ok_return(interval->cb.obj); 2007 } 2008 #endif 2009 2010 2011 /** 2012 * Represents a uloop signal Unix process signal handler as returned by 2013 * {@link module:uloop#signal|signal()}. 2014 * 2015 * @class module:uloop.signal 2016 * @hideconstructor 2017 * 2018 * @see {@link module:uloop#signal|signal()} 2019 * 2020 * @example 2021 * 2022 * const sighandler = uloop.signal(…); 2023 * 2024 * sighandler.signo(); 2025 * sighandler.delete(); 2026 */ 2027 #ifdef HAVE_ULOOP_SIGNAL 2028 typedef struct { 2029 uc_uloop_cb_t cb; 2030 struct uloop_signal signal; 2031 } uc_uloop_signal_t; 2032 2033 static int 2034 uc_uloop_signal_clear(uc_uloop_signal_t *signal) 2035 { 2036 int rv = uloop_signal_delete(&signal->signal); 2037 2038 uc_uloop_cb_free(&signal->cb); 2039 2040 return rv; 2041 } 2042 2043 /** 2044 * Returns the associated signal number. 2045 * 2046 * This method returns the signal number that this uloop signal handler is 2047 * configured to respond to. 2048 * 2049 * @function module:uloop.signal#signo 2050 * 2051 * @returns {number} 2052 * The signal number that this handler is responding to. 2053 * 2054 * @example 2055 * // Get the signal number that the uloop signal handler is responding to 2056 * const sighandler = uloop.signal("SIGINT", () => printf("Cought INT\n")); 2057 * printf("Signal number: %d\n", sighandler.signo()); 2058 */ 2059 static uc_value_t * 2060 uc_uloop_signal_signo(uc_vm_t *vm, size_t nargs) 2061 { 2062 uc_uloop_signal_t *signal = uc_fn_thisval("uloop.signal"); 2063 2064 if (!signal) 2065 err_return(EINVAL); 2066 2067 ok_return(ucv_int64_new(signal->signal.signo)); 2068 } 2069 2070 /** 2071 * Uninstalls the signal handler. 2072 * 2073 * This method uninstalls the signal handler, restoring the previous or default 2074 * handler for the signal, and releasing any associated resources. 2075 * 2076 * @function module:uloop.signal#delete 2077 * 2078 * @returns {boolean} 2079 * Returns `true` on success. 2080 * 2081 * @example 2082 * // Uninstall the signal handler and restore the previous/default handler 2083 * const sighandler = uloop.signal(…); 2084 * sighandler.delete(); 2085 */ 2086 static uc_value_t * 2087 uc_uloop_signal_delete(uc_vm_t *vm, size_t nargs) 2088 { 2089 uc_uloop_signal_t *signal = uc_fn_thisval("uloop.signal"); 2090 int rv; 2091 2092 if (!signal) 2093 err_return(EINVAL); 2094 2095 rv = uc_uloop_signal_clear(signal); 2096 2097 if (rv != 0) 2098 err_return(EINVAL); 2099 2100 ok_return(ucv_boolean_new(true)); 2101 } 2102 2103 static void 2104 uc_uloop_signal_cb(struct uloop_signal *usig) 2105 { 2106 uc_uloop_signal_t *signal = container_of(usig, uc_uloop_signal_t, signal); 2107 2108 uc_uloop_cb_invoke(&signal->cb, NULL); 2109 } 2110 2111 static int 2112 parse_signo(uc_value_t *sigspec) 2113 { 2114 if (ucv_type(sigspec) == UC_STRING) { 2115 const char *signame = ucv_string_get(sigspec); 2116 2117 if (!strncasecmp(signame, "SIG", 3)) 2118 signame += 3; 2119 2120 for (size_t i = 0; i < UC_SYSTEM_SIGNAL_COUNT; i++) { 2121 if (!uc_system_signal_names[i]) 2122 continue; 2123 2124 if (strcasecmp(uc_system_signal_names[i], signame)) 2125 continue; 2126 2127 return i; 2128 } 2129 } 2130 2131 uc_value_t *signum = ucv_to_number(sigspec); 2132 int64_t signo = ucv_int64_get(signum); 2133 ucv_put(signum); 2134 2135 if (signo < 1 || signo >= UC_SYSTEM_SIGNAL_COUNT) 2136 return -1; 2137 2138 return signo; 2139 } 2140 2141 /** 2142 * Creates a signal instance for handling Unix signals. 2143 * 2144 * This function creates a signal instance for handling Unix signals. 2145 * It takes the signal name string (with or without "SIG" prefix) or signal 2146 * number, and a callback function to be invoked when the specified Unix signal 2147 * is caught. 2148 * 2149 * @function module:uloop#signal 2150 * 2151 * @param {string|number} signal 2152 * The signal name string (with or without "SIG" prefix) or signal number. 2153 * 2154 * @param {Function} callback 2155 * The callback function to be invoked when the specified Unix signal is caught. 2156 * 2157 * @returns {?module:uloop.signal} 2158 * Returns a signal instance representing the installed signal handler. 2159 * Returns `null` when the signal or callback arguments are invalid. 2160 * 2161 * @example 2162 * // Create a signal instance for handling SIGINT 2163 * const mySignal = uloop.signal("SIGINT", () => { 2164 * printf("SIGINT caught!\n"); 2165 * }); 2166 */ 2167 static uc_value_t * 2168 uc_uloop_signal(uc_vm_t *vm, size_t nargs) 2169 { 2170 int signo = parse_signo(uc_fn_arg(0)); 2171 uc_value_t *callback = uc_fn_arg(1); 2172 uc_uloop_signal_t *signal; 2173 2174 if (signo == -1 || !ucv_is_callable(callback)) 2175 err_return(EINVAL); 2176 2177 signal = uc_uloop_alloc(vm, "uloop.signal", sizeof(*signal), callback); 2178 signal->signal.signo = signo; 2179 signal->signal.cb = uc_uloop_signal_cb; 2180 2181 uloop_signal_add(&signal->signal); 2182 2183 ok_return(signal->cb.obj); 2184 } 2185 #endif 2186 2187 static uc_value_t * 2188 uc_uloop_guard(uc_vm_t *vm, size_t nargs) 2189 { 2190 uc_value_t *arg = uc_fn_arg(0); 2191 2192 if (!nargs) 2193 return ucv_get(uc_vm_registry_get(vm, "uloop.ex_handler")); 2194 2195 if (arg && !ucv_is_callable(arg)) 2196 return NULL; 2197 2198 uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg)); 2199 2200 return ucv_boolean_new(true); 2201 } 2202 2203 2204 static const uc_function_list_t timer_fns[] = { 2205 { "set", uc_uloop_timer_set }, 2206 { "remaining", uc_uloop_timer_remaining }, 2207 { "cancel", uc_uloop_timer_cancel }, 2208 }; 2209 2210 static const uc_function_list_t handle_fns[] = { 2211 { "fileno", uc_uloop_handle_fileno }, 2212 { "handle", uc_uloop_handle_handle }, 2213 { "delete", uc_uloop_handle_delete }, 2214 }; 2215 2216 static const uc_function_list_t process_fns[] = { 2217 { "pid", uc_uloop_process_pid }, 2218 { "delete", uc_uloop_process_delete }, 2219 }; 2220 2221 static const uc_function_list_t task_fns[] = { 2222 { "pid", uc_uloop_task_pid }, 2223 { "kill", uc_uloop_task_kill }, 2224 { "finished", uc_uloop_task_finished }, 2225 }; 2226 2227 static const uc_function_list_t pipe_fns[] = { 2228 { "send", uc_uloop_pipe_send }, 2229 { "receive", uc_uloop_pipe_receive }, 2230 { "sending", uc_uloop_pipe_sending }, 2231 { "receiving", uc_uloop_pipe_receiving }, 2232 }; 2233 2234 #ifdef HAVE_ULOOP_INTERVAL 2235 static const uc_function_list_t interval_fns[] = { 2236 { "set", uc_uloop_interval_set }, 2237 { "remaining", uc_uloop_interval_remaining }, 2238 { "expirations", 2239 uc_uloop_interval_expirations }, 2240 { "cancel", uc_uloop_interval_cancel }, 2241 }; 2242 #endif 2243 2244 #ifdef HAVE_ULOOP_SIGNAL 2245 static const uc_function_list_t signal_fns[] = { 2246 { "signo", uc_uloop_signal_signo }, 2247 { "delete", uc_uloop_signal_delete }, 2248 }; 2249 #endif 2250 2251 static const uc_function_list_t global_fns[] = { 2252 { "error", uc_uloop_error }, 2253 { "init", uc_uloop_init }, 2254 { "run", uc_uloop_run }, 2255 { "timer", uc_uloop_timer }, 2256 { "handle", uc_uloop_handle }, 2257 { "process", uc_uloop_process }, 2258 { "task", uc_uloop_task }, 2259 { "cancelling", uc_uloop_cancelling }, 2260 { "running", uc_uloop_running }, 2261 { "done", uc_uloop_done }, 2262 { "end", uc_uloop_end }, 2263 #ifdef HAVE_ULOOP_INTERVAL 2264 { "interval", uc_uloop_interval }, 2265 #endif 2266 #ifdef HAVE_ULOOP_SIGNAL 2267 { "signal", uc_uloop_signal }, 2268 #endif 2269 { "guard", uc_uloop_guard }, 2270 }; 2271 2272 2273 static void close_timer(void *ud) 2274 { 2275 uc_uloop_timeout_clear(ud); 2276 } 2277 2278 static void close_handle(void *ud) 2279 { 2280 uc_uloop_handle_clear(ud); 2281 } 2282 2283 static void close_process(void *ud) 2284 { 2285 uc_uloop_process_clear(ud); 2286 } 2287 2288 static void close_task(void *ud) 2289 { 2290 uc_uloop_task_clear(ud); 2291 } 2292 2293 static void close_pipe(void *ud) 2294 { 2295 uc_uloop_pipe_t *pipe = ud; 2296 2297 if (!pipe) 2298 return; 2299 2300 close(pipe->input); 2301 close(pipe->output); 2302 2303 free(pipe); 2304 } 2305 2306 #ifdef HAVE_ULOOP_INTERVAL 2307 static void close_interval(void *ud) 2308 { 2309 uc_uloop_interval_clear(ud); 2310 } 2311 #endif 2312 2313 #ifdef HAVE_ULOOP_SIGNAL 2314 static void close_signal(void *ud) 2315 { 2316 uc_uloop_signal_clear(ud); 2317 } 2318 #endif 2319 2320 2321 static struct { 2322 struct uloop_fd ufd; 2323 uc_vm_t *vm; 2324 } signal_handle; 2325 2326 static void 2327 uc_uloop_vm_signal_cb(struct uloop_fd *ufd, unsigned int events) 2328 { 2329 if (uc_vm_signal_dispatch(signal_handle.vm) != EXCEPTION_NONE) 2330 uloop_end(); 2331 } 2332 2333 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 2334 { 2335 int signal_fd; 2336 2337 uc_function_list_register(scope, global_fns); 2338 2339 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x)) 2340 2341 /** 2342 * @typedef 2343 * @name Event Mode Constants 2344 * @description 2345 * The `ULOOP_*` constants are passed as bitwise OR-ed number to the 2346 * {@link module:uloop.handle#handle|handle()} function to specify the IO 2347 * events that should be monitored on the given handle. 2348 * @property {number} ULOOP_READ - File or socket is readable. 2349 * @property {number} ULOOP_WRITE - File or socket is writable. 2350 * @property {number} ULOOP_EDGE_TRIGGER - Enable edge-triggered event mode. 2351 * @property {number} ULOOP_BLOCKING - Do not make descriptor non-blocking. 2352 */ 2353 ADD_CONST(ULOOP_READ); 2354 ADD_CONST(ULOOP_WRITE); 2355 ADD_CONST(ULOOP_EDGE_TRIGGER); 2356 ADD_CONST(ULOOP_BLOCKING); 2357 2358 uc_type_declare(vm, "uloop.timer", timer_fns, close_timer); 2359 uc_type_declare(vm, "uloop.handle", handle_fns, close_handle); 2360 uc_type_declare(vm, "uloop.process", process_fns, close_process); 2361 uc_type_declare(vm, "uloop.task", task_fns, close_task); 2362 uc_type_declare(vm, "uloop.pipe", pipe_fns, close_pipe); 2363 2364 #ifdef HAVE_ULOOP_INTERVAL 2365 uc_type_declare(vm, "uloop.interval", interval_fns, close_interval); 2366 #endif 2367 2368 #ifdef HAVE_ULOOP_SIGNAL 2369 uc_type_declare(vm, "uloop.signal", signal_fns, close_signal); 2370 #endif 2371 2372 signal_fd = uc_vm_signal_notifyfd(vm); 2373 2374 if (signal_fd != -1 && uloop_init() == 0) { 2375 signal_handle.vm = vm; 2376 signal_handle.ufd.cb = uc_uloop_vm_signal_cb; 2377 signal_handle.ufd.fd = signal_fd; 2378 2379 uloop_fd_add(&signal_handle.ufd, ULOOP_READ); 2380 } 2381 } 2382
This page was automatically generated by LXR 0.3.1. • OpenWrt