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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt