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