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