• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/uloop.c

  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