• 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 <unistd.h>
 68 #include <limits.h>
 69 #include <fcntl.h>
 70 #include <sys/wait.h>
 71 
 72 #include <libubox/uloop.h>
 73 
 74 #include "ucode/module.h"
 75 #include "ucode/internal/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         int status = WIFEXITED(exitcode) ? WEXITSTATUS(exitcode) :
965                 WIFSIGNALED(exitcode) ? -WTERMSIG(exitcode) : -1;
966         uc_value_t *e = ucv_int64_new(status);
967 
968         uc_uloop_cb_invoke(&process->cb, &e, 1);
969         uc_uloop_process_clear(process);
970         ucv_put(e);
971 }
972 
973 /**
974  * Creates a process instance for executing external programs.
975  *
976  * This function creates a process instance for executing external programs.
977  * It takes the executable path string, an optional string array as the argument
978  * vector, an optional dictionary describing environment variables, and a
979  * callback function to be invoked when the invoked process ends.
980  *
981  * @function module:uloop#process
982  *
983  * @param {string} executable
984  * The path to the executable program.
985  *
986  * @param {string[]} [args]
987  * Optional. An array of strings representing the arguments passed to the
988  * executable.
989  *
990  * @param {Object<string, *>} [env]
991  * Optional. A dictionary describing environment variables for the process.
992  *
993  * @param {Function} callback
994  * The callback function to be invoked when the invoked process ends.
995  * Receives the exit code for a normally exited process, or the negative
996  * signal number if the process was terminated by a signal.
997  *
998  * @returns {?module:uloop.process}
999  * Returns a process instance for executing external programs.
1000  * Returns `null` on error, e.g. due to `exec()` failure or invalid arguments.
1001  *
1002  * @example
1003  * // Create a process instance for executing 'ls' command
1004  * const myProcess = uloop.process("/bin/ls", ["-l", "/tmp"], null, (code) => {
1005  *     printf(`Process exited with code ${code}\n`);
1006  * });
1007  */
1008 static uc_value_t *
1009 uc_uloop_process(uc_vm_t *vm, size_t nargs)
1010 {
1011         uc_value_t *executable = uc_fn_arg(0);
1012         uc_value_t *arguments = uc_fn_arg(1);
1013         uc_value_t *env_arg = uc_fn_arg(2);
1014         uc_value_t *callback = uc_fn_arg(3);
1015         uc_uloop_process_t *process;
1016         uc_stringbuf_t *buf;
1017         char **argp, **envp;
1018         pid_t pid;
1019         size_t i;
1020 
1021         if (ucv_type(executable) != UC_STRING ||
1022             (arguments && ucv_type(arguments) != UC_ARRAY) ||
1023             (env_arg && ucv_type(env_arg) != UC_OBJECT) ||
1024             !ucv_is_callable(callback)) {
1025                 err_return(EINVAL);
1026         }
1027 
1028         pid = fork();
1029 
1030         if (pid == -1)
1031                 err_return(errno);
1032 
1033         if (pid == 0) {
1034                 argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *));
1035                 envp = environ;
1036 
1037                 if (env_arg) {
1038                         envp = calloc(ucv_object_length(env_arg) + 1, sizeof(char *));
1039                         i = 0;
1040                         ucv_object_foreach(env_arg, envk, envv) {
1041                                 buf = xprintbuf_new();
1042 
1043                                 ucv_stringbuf_printf(buf, "%s=", envk);
1044                                 ucv_to_stringbuf(vm, buf, envv, false);
1045 
1046                                 envp[i++] = buf->buf;
1047 
1048                                 free(buf);
1049                         }
1050                 }
1051 
1052                 if (!argp || !envp)
1053                         _exit(-1);
1054 
1055                 argp[0] = ucv_to_string(vm, executable);
1056 
1057                 for (i = 0; i < ucv_array_length(arguments); i++)
1058                         argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i));
1059 
1060                 execvpe((const char *)ucv_string_get(executable),
1061                         (char * const *)argp, (char * const *)envp);
1062 
1063                 _exit(-1);
1064         }
1065 
1066         process = uc_uloop_alloc(vm, "uloop.process", sizeof(*process), callback);
1067         process->process.pid = pid;
1068         process->process.cb = uc_uloop_process_cb;
1069         uloop_process_add(&process->process);
1070 
1071         ok_return(process->cb.obj);
1072 }
1073 
1074 
1075 static bool
1076 readall(int fd, void *buf, size_t len)
1077 {
1078         ssize_t rlen;
1079 
1080         while (len > 0) {
1081                 rlen = read(fd, buf, len);
1082 
1083                 if (rlen == -1) {
1084                         if (errno == EINTR)
1085                                 continue;
1086 
1087                         return false;
1088                 }
1089 
1090                 if (rlen == 0) {
1091                         errno = EINTR;
1092 
1093                         return false;
1094                 }
1095 
1096                 buf += rlen;
1097                 len -= rlen;
1098         }
1099 
1100         return true;
1101 }
1102 
1103 static bool
1104 writeall(int fd, void *buf, size_t len)
1105 {
1106         ssize_t wlen;
1107 
1108         while (len > 0) {
1109                 wlen = write(fd, buf, len);
1110 
1111                 if (wlen == -1) {
1112                         if (errno == EINTR)
1113                                 continue;
1114 
1115                         return false;
1116                 }
1117 
1118                 buf += wlen;
1119                 len -= wlen;
1120         }
1121 
1122         return true;
1123 }
1124 
1125 
1126 /**
1127  * Represents a uloop task communication pipe instance, passed as sole argument
1128  * to the task function by {@link module:uloop#task|task()}.
1129  *
1130  * @class module:uloop.pipe
1131  * @hideconstructor
1132  *
1133  * @see {@link module:uloop#task|task()}
1134  *
1135  * @example *
1136  * const task = uloop.task((pipe) => {
1137  *     …
1138  *     pipe.send();
1139  *     …
1140  *     pipe.receive();
1141  *     …
1142  * }, …);
1143  */
1144 typedef struct {
1145         int input;
1146         int output;
1147         bool has_sender;
1148         bool has_receiver;
1149 } uc_uloop_pipe_t;
1150 
1151 static uc_value_t *
1152 uc_uloop_pipe_send_common(uc_vm_t *vm, uc_value_t *msg, int fd)
1153 {
1154         uc_stringbuf_t *buf;
1155         size_t len;
1156         bool rv;
1157 
1158         buf = xprintbuf_new();
1159 
1160         printbuf_memset(buf, 0, 0, sizeof(len));
1161         ucv_to_stringbuf(vm, buf, msg, true);
1162 
1163         len = printbuf_length(buf);
1164         memcpy(buf->buf, &len, sizeof(len));
1165 
1166         rv = writeall(fd, buf->buf, len);
1167 
1168         printbuf_free(buf);
1169 
1170         if (!rv)
1171                 err_return(errno);
1172 
1173         ok_return(ucv_boolean_new(true));
1174 }
1175 
1176 /**
1177  * Sends a serialized message to the task handle.
1178  *
1179  * This method serializes the provided message and sends it over the task
1180  * communication pipe. In the main thread, the message is deserialized and
1181  * passed as an argument to the output callback function registered with the
1182  * task handle.
1183  *
1184  * @function module:uloop.pipe#send
1185  *
1186  * @param {*} msg
1187  * The message to be serialized and sent over the pipe. It can be of arbitrary type.
1188  *
1189  * @returns {?boolean}
1190  * Returns `true` on success, indicating that the message was successfully sent
1191  * over the pipe. Returns `null` on error, such as when there's no output
1192  * callback registered with the task handle.
1193  *
1194  * @example
1195  * // Send a message over the uloop pipe
1196  * const success = pipe.send(message);
1197  *
1198  * if (success)
1199  *     printf("Message sent successfully\n");
1200  * else
1201  *     die(`Error sending message: ${uloop.error()}\n`);
1202  */
1203 static uc_value_t *
1204 uc_uloop_pipe_send(uc_vm_t *vm, size_t nargs)
1205 {
1206         uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe");
1207         uc_value_t *msg = uc_fn_arg(0);
1208 
1209         if (!pipe)
1210                 err_return(EINVAL);
1211 
1212         if (!pipe->has_receiver)
1213                 err_return(EPIPE);
1214 
1215         ok_return(uc_uloop_pipe_send_common(vm, msg, pipe->output));
1216 }
1217 
1218 static bool
1219 uc_uloop_pipe_receive_common(uc_vm_t *vm, int fd, uc_value_t **res, bool skip)
1220 {
1221         enum json_tokener_error err = json_tokener_error_parse_eof;
1222         json_tokener *tok = NULL;
1223         json_object *jso = NULL;
1224         char buf[1024];
1225         ssize_t rlen;
1226         size_t len;
1227 
1228         *res = NULL;
1229 
1230         if (!readall(fd, &len, sizeof(len)))
1231                 err_return(errno);
1232 
1233         /* message length 0 is special, means input requested on other pipe */
1234         if (len == 0)
1235                 err_return(ENODATA);
1236 
1237         /* valid messages should be at least sizeof(len) plus one byte of payload */
1238         if (len <= sizeof(len))
1239                 err_return(EINVAL);
1240 
1241         len -= sizeof(len);
1242 
1243         while (len > 0) {
1244                 rlen = read(fd, buf, len < sizeof(buf) ? len : sizeof(buf));
1245 
1246                 if (rlen == -1) {
1247                         if (errno == EINTR)
1248                                 continue;
1249 
1250                         goto read_fail;
1251                 }
1252 
1253                 /* premature EOF */
1254                 if (rlen == 0) {
1255                         errno = EPIPE;
1256                         goto read_fail;
1257                 }
1258 
1259                 if (!skip) {
1260                         if (!tok)
1261                                 tok = xjs_new_tokener();
1262 
1263                         jso = json_tokener_parse_ex(tok, buf, rlen);
1264                         err = json_tokener_get_error(tok);
1265                 }
1266 
1267                 len -= rlen;
1268         }
1269 
1270         if (!skip) {
1271                 if (err == json_tokener_continue) {
1272                         jso = json_tokener_parse_ex(tok, "\0", 1);
1273                         err = json_tokener_get_error(tok);
1274                 }
1275 
1276                 json_tokener_free(tok);
1277 
1278                 if (err != json_tokener_success) {
1279                         errno = EINVAL;
1280                         goto read_fail;
1281                 }
1282 
1283                 *res = ucv_from_json(vm, jso);
1284 
1285                 json_object_put(jso);
1286         }
1287 
1288         return true;
1289 
1290 read_fail:
1291         if (tok)
1292                 json_tokener_free(tok);
1293 
1294         json_object_put(jso);
1295         err_return(errno);
1296 }
1297 
1298 /**
1299  * Reads input from the task handle.
1300  *
1301  * This method reads input from the task communication pipe. The input callback
1302  * function registered with the task handle is invoked to return the input data,
1303  * which is then serialized, sent over the pipe, and deserialized by the receive
1304  * method.
1305  *
1306  * @function module:uloop.pipe#receive
1307  *
1308  * @returns {?*}
1309  * Returns the deserialized message read from the task communication pipe.
1310  * Returns `null` on error, such as when there's no input callback registered
1311  * on the task handle.
1312  *
1313  * @example
1314  * // Read input from the task communication pipe
1315  * const message = pipe.receive();
1316  *
1317  * if (message !== null)
1318  *     printf("Received message: %s\n", message);
1319  * else
1320  *     die(`Error receiving message: ${uloop.error()}\n`);
1321  */
1322 static uc_value_t *
1323 uc_uloop_pipe_receive(uc_vm_t *vm, size_t nargs)
1324 {
1325         uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe");
1326         uc_value_t *rv;
1327         size_t len = 0;
1328 
1329         if (!pipe)
1330                 err_return(EINVAL);
1331 
1332         if (!pipe->has_sender)
1333                 err_return(EPIPE);
1334 
1335         /* send zero-length message to signal input request */
1336         writeall(pipe->output, &len, sizeof(len));
1337 
1338         /* receive input message */
1339         uc_uloop_pipe_receive_common(vm, pipe->input, &rv, false);
1340 
1341         return rv;
1342 }
1343 
1344 /**
1345  * Checks if the task handle provides input.
1346  *
1347  * This method checks if the task handle has an input callback  registered.
1348  * It returns a boolean value indicating whether an input callback is present.
1349  *
1350  * @function module:uloop.pipe#sending
1351  *
1352  * @returns {boolean}
1353  * Returns `true` if the remote task handle has an input callback
1354  * registered, otherwise returns `false`.
1355  *
1356  * @example
1357  * // Check if the remote task handle has an input callback
1358  * const hasInputCallback = pipe.sending();
1359  *
1360  * if (hasInputCallback)
1361  *     printf("Input callback is registered on task handle\n");
1362  * else
1363  *     printf("No input callback on the task handle\n");
1364  */
1365 static uc_value_t *
1366 uc_uloop_pipe_sending(uc_vm_t *vm, size_t nargs)
1367 {
1368         uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe");
1369 
1370         if (!pipe)
1371                 err_return(EINVAL);
1372 
1373         ok_return(ucv_boolean_new(pipe->has_sender));
1374 }
1375 
1376 /**
1377  * Checks if the task handle reads output.
1378  *
1379  * This method checks if the task handle has an output callback registered.
1380  * It returns a boolean value indicating whether an output callback is present.
1381  *
1382  * @function module:uloop.pipe#receiving
1383  *
1384  * @returns {boolean}
1385  * Returns `true` if the task handle has an output callback registered,
1386  * otherwise returns `false`.
1387  *
1388  * @example
1389  * // Check if the task handle has an output callback
1390  * const hasOutputCallback = pipe.receiving();
1391  *
1392  * if (hasOutputCallback)
1393  *     printf("Output callback is registered on task handle\n");
1394  * else
1395  *     printf("No output callback on the task handle\n");
1396  */
1397 static uc_value_t *
1398 uc_uloop_pipe_receiving(uc_vm_t *vm, size_t nargs)
1399 {
1400         uc_uloop_pipe_t *pipe = uc_fn_thisval("uloop.pipe");
1401 
1402         if (!pipe)
1403                 err_return(EINVAL);
1404 
1405         ok_return(ucv_boolean_new(pipe->has_receiver));
1406 }
1407 
1408 
1409 /**
1410  * Represents a uloop task instance as returned by
1411  * {@link module:uloop#task|task()}.
1412  *
1413  * @class module:uloop.task
1414  * @hideconstructor
1415  *
1416  * @see {@link module:uloop#task|task()}
1417  *
1418  * @example
1419  *
1420  * const task = uloop.task(…);
1421  *
1422  * task.pid();
1423  * task.finished();
1424  *
1425  * task.kill();
1426  */
1427 typedef struct {
1428         uc_uloop_cb_t cb;
1429         struct uloop_process process;
1430         struct uloop_fd output;
1431         bool finished;
1432         int input_fd;
1433         uc_value_t *input_cb;
1434         uc_value_t *output_cb;
1435 } uc_uloop_task_t;
1436 
1437 static int
1438 patch_devnull(int fd, bool write)
1439 {
1440         int devnull = open("/dev/null", write ? O_WRONLY : O_RDONLY);
1441 
1442         if (devnull != -1) {
1443                 dup2(fd, devnull);
1444                 close(fd);
1445         }
1446 
1447         return devnull;
1448 }
1449 
1450 static void
1451 uloop_fd_close(struct uloop_fd *fd) {
1452         if (fd->fd == -1)
1453                 return;
1454 
1455         uloop_fd_delete(fd);
1456         close(fd->fd);
1457         fd->fd = -1;
1458 }
1459 
1460 static void
1461 uc_uloop_task_clear(uc_uloop_task_t *task)
1462 {
1463         if (task->input_fd >= 0) {
1464                 close(task->input_fd);
1465                 task->input_fd = -1;
1466 
1467                 uloop_fd_close(&task->output);
1468                 uloop_process_delete(&task->process);
1469         }
1470 
1471         uc_uloop_cb_free(&task->cb);
1472 }
1473 
1474 /**
1475  * Returns the process ID.
1476  *
1477  * This method returns the process ID (PID) of the underlying forked process
1478  * launched by {@link module:uloop#task|task().
1479  *
1480  * @function module:uloop.task#pid
1481  *
1482  * @returns {number}
1483  * The process ID (PID) of the forked task process.
1484  *
1485  * @example
1486  * const task = uloop.task(…);
1487  *
1488  * printf("Process ID: %d\n", task.pid());
1489  */
1490 static uc_value_t *
1491 uc_uloop_task_pid(uc_vm_t *vm, size_t nargs)
1492 {
1493         uc_uloop_task_t *task = uc_fn_thisval("uloop.task");
1494 
1495         if (!task)
1496                 err_return(EINVAL);
1497 
1498         if (task->finished)
1499                 err_return(ESRCH);
1500 
1501         ok_return(ucv_int64_new(task->process.pid));
1502 }
1503 
1504 /**
1505  * Terminates the task process.
1506  *
1507  * This method terminates the task process. It sends a termination signal to
1508  * the task process, causing it to exit. Returns `true` on success, indicating
1509  * that the task process was successfully terminated. Returns `null` on error,
1510  * such as when the task process has already terminated.
1511  *
1512  * @function module:uloop.task#kill
1513  *
1514  * @returns {?boolean}
1515  * Returns `true` when the task process was successfully terminated.
1516  * Returns `null` on error, such as when the process has already terminated.
1517  *
1518  * @example
1519  * // Terminate the task process
1520  * const success = task.kill();
1521  *
1522  * if (success)
1523  *     printf("Task process terminated successfully\n");
1524  * else
1525  *     die(`Error terminating task process: ${uloop.error()}\n`);
1526  */
1527 static uc_value_t *
1528 uc_uloop_task_kill(uc_vm_t *vm, size_t nargs)
1529 {
1530         uc_uloop_task_t *task = uc_fn_thisval("uloop.task");
1531         int rv;
1532 
1533         if (!task)
1534                 err_return(EINVAL);
1535 
1536         if (task->finished)
1537                 err_return(ESRCH);
1538 
1539         rv = kill(task->process.pid, SIGTERM);
1540 
1541         if (rv == -1)
1542                 err_return(errno);
1543 
1544         ok_return(ucv_boolean_new(true));
1545 }
1546 
1547 /**
1548  * Checks if the task ran to completion.
1549  *
1550  * This method checks if the task function has already run to completion.
1551  * It returns a boolean value indicating whether the task function has finished
1552  * executing.
1553  *
1554  * @function module:uloop.task#finished
1555  *
1556  * @returns {boolean}
1557  * Returns `true` if the task function has already run to completion, otherwise
1558  * returns `false`.
1559  *
1560  * @example
1561  * // Check if the task function has finished executing
1562  * const isFinished = task.finished();
1563  *
1564  * if (isFinished)
1565  *     printf("Task function has finished executing\n");
1566  * else
1567  *     printf("Task function is still running\n");
1568  */
1569 static uc_value_t *
1570 uc_uloop_task_finished(uc_vm_t *vm, size_t nargs)
1571 {
1572         uc_uloop_task_t *task = uc_fn_thisval("uloop.task");
1573 
1574         if (!task)
1575                 err_return(EINVAL);
1576 
1577         ok_return(ucv_boolean_new(task->finished));
1578 }
1579 
1580 static void
1581 uc_uloop_task_output_cb(struct uloop_fd *fd, unsigned int flags)
1582 {
1583         uc_uloop_task_t *task = container_of(fd, uc_uloop_task_t, output);
1584         uc_value_t *obj = task->cb.obj;
1585         uc_vm_t *vm = task->cb.vm;
1586         uc_value_t *msg = NULL;
1587 
1588         if (flags & ULOOP_READ) {
1589                 while (true) {
1590                         if (!uc_uloop_pipe_receive_common(vm, fd->fd, &msg, !task->output_cb)) {
1591                                 /* input requested */
1592                                 if (last_error == ENODATA) {
1593                                         uc_vm_stack_push(vm, ucv_get(obj));
1594                                         uc_vm_stack_push(vm, ucv_get(task->input_cb));
1595 
1596                                         if (!uc_uloop_vm_call(vm, true, 0))
1597                                                 return;
1598 
1599                                         msg = uc_vm_stack_pop(vm);
1600                                         uc_uloop_pipe_send_common(vm, msg, task->input_fd);
1601                                         ucv_put(msg);
1602 
1603                                         continue;
1604                                 }
1605 
1606                                 /* error */
1607                                 break;
1608                         }
1609 
1610                         if (task->output_cb) {
1611                                 uc_vm_stack_push(vm, ucv_get(obj));
1612                                 uc_vm_stack_push(vm, ucv_get(task->output_cb));
1613                                 uc_vm_stack_push(vm, msg);
1614 
1615                                 if (!uc_uloop_vm_call(vm, true, 1))
1616                                         return;
1617 
1618                                 ucv_put(uc_vm_stack_pop(vm));
1619                         }
1620                         else {
1621                                 ucv_put(msg);
1622                         }
1623                 }
1624         }
1625 
1626         if (!fd->registered && task->finished)
1627                 uc_uloop_task_clear(task);
1628 }
1629 
1630 static void
1631 uc_uloop_task_process_cb(struct uloop_process *proc, int exitcode)
1632 {
1633         uc_uloop_task_t *task = container_of(proc, uc_uloop_task_t, process);
1634 
1635         task->finished = true;
1636 
1637         uc_uloop_task_output_cb(&task->output, ULOOP_READ);
1638 }
1639 
1640 /**
1641  * Creates a task instance for executing background tasks.
1642  *
1643  * This function creates a task instance for executing background tasks.
1644  * It takes the task function to be invoked as a background process,
1645  * an optional output callback function to be invoked when output is received
1646  * from the task, and an optional input callback function to be invoked
1647  * when input is required by the task.
1648  *
1649  * @function module:uloop#task
1650  *
1651  * @param {Function} taskFunction
1652  * The task function to be invoked as a background process.
1653  *
1654  * @param {Function} [outputCallback]
1655  * Optional. The output callback function to be invoked when output is received
1656  * from the task. It is invoked with the output data as the argument.
1657  *
1658  * @param {Function} [inputCallback]
1659  * Optional. The input callback function to be invoked when input is required
1660  * by the task. It is invoked with a function to send input to the task
1661  * as the argument.
1662  *
1663  * @returns {?module:uloop.task}
1664  * Returns a task instance for executing background tasks.
1665  * Returns `null` on error, e.g. due to fork failure or invalid arguments.
1666  *
1667  * @example
1668  * // Create a task instance for executing a background task
1669  * const myTask = uloop.task(
1670  *     (pipe) => {
1671  *         // Task logic
1672  *         pipe.send("Hello from the task\n");
1673  *         const input = pipe.receive();
1674  *         printf(`Received input from main thread: ${input}\n`);
1675  *     },
1676  *     (output) => {
1677  *         // Output callback, invoked when task function calls pipe.send()
1678  *         printf(`Received output from task: ${output}\n`);
1679  *     },
1680  *     () => {
1681  *         // Input callback, invoked when task function calls pipe.receive()
1682  *         return "Input from main thread\n";
1683  *     }
1684  * );
1685  */
1686 static uc_value_t *
1687 uc_uloop_task(uc_vm_t *vm, size_t nargs)
1688 {
1689         uc_value_t *func = uc_fn_arg(0);
1690         uc_value_t *output_cb = uc_fn_arg(1);
1691         uc_value_t *input_cb = uc_fn_arg(2);
1692         int outpipe[2] = { -1, -1 };
1693         int inpipe[2] = { -1, -1 };
1694         uc_value_t *res, *cbs, *p;
1695         uc_uloop_pipe_t *tpipe;
1696         uc_uloop_task_t *task;
1697         pid_t pid;
1698         int err;
1699 
1700         if (!ucv_is_callable(func) ||
1701             (output_cb && !ucv_is_callable(output_cb)) ||
1702             (input_cb && !ucv_is_callable(input_cb)))
1703             err_return(EINVAL);
1704 
1705         if (pipe(outpipe) == -1 || pipe(inpipe) == -1) {
1706                 err = errno;
1707 
1708                 close(outpipe[0]); close(outpipe[1]);
1709                 close(inpipe[0]); close(inpipe[1]);
1710 
1711                 err_return(err);
1712         }
1713 
1714         pid = fork();
1715 
1716         if (pid == -1)
1717                 err_return(errno);
1718 
1719         if (pid == 0) {
1720                 uloop_done();
1721 
1722                 patch_devnull(0, false);
1723                 patch_devnull(1, true);
1724                 patch_devnull(2, true);
1725 
1726                 vm->output = fdopen(1, "w");
1727 
1728                 close(inpipe[1]);
1729                 close(outpipe[0]);
1730 
1731                 tpipe = xalloc(sizeof(*tpipe));
1732                 tpipe->input = inpipe[0];
1733                 tpipe->output = outpipe[1];
1734                 tpipe->has_sender = input_cb;
1735                 tpipe->has_receiver = output_cb;
1736 
1737                 p = ucv_resource_create(vm, "uloop.pipe", tpipe);
1738 
1739                 uc_vm_stack_push(vm, func);
1740                 uc_vm_stack_push(vm, ucv_get(p));
1741 
1742                 if (uc_uloop_vm_call(vm, false, 1)) {
1743                         res = uc_vm_stack_pop(vm);
1744                         uc_uloop_pipe_send_common(vm, res, tpipe->output);
1745                         ucv_put(res);
1746                 }
1747 
1748                 ucv_put(p);
1749 
1750                 _exit(0);
1751         }
1752 
1753         close(inpipe[0]);
1754         close(outpipe[1]);
1755 
1756         task = uc_uloop_alloc(vm, "uloop.task", sizeof(*task), func);
1757         task->process.pid = pid;
1758         task->process.cb = uc_uloop_task_process_cb;
1759 
1760         task->output.fd = outpipe[0];
1761         task->output.cb = uc_uloop_task_output_cb;
1762         task->output_cb = output_cb;
1763         uloop_fd_add(&task->output, ULOOP_READ);
1764 
1765         if (input_cb) {
1766                 task->input_fd = inpipe[1];
1767                 task->input_cb = input_cb;
1768         }
1769         else {
1770                 task->input_fd = -1;
1771                 close(inpipe[1]);
1772         }
1773 
1774         uloop_process_add(&task->process);
1775 
1776         cbs = ucv_array_new(NULL);
1777         ucv_array_set(cbs, 0, ucv_get(output_cb));
1778         ucv_array_set(cbs, 1, ucv_get(input_cb));
1779         ucv_resource_value_set(task->cb.obj, 1, cbs);
1780 
1781         ok_return(task->cb.obj);
1782 }
1783 
1784 
1785 /**
1786  * Represents a uloop interval timer instance as returned by
1787  * {@link module:uloop#interval|interval()}.
1788  *
1789  * @class module:uloop.interval
1790  * @hideconstructor
1791  *
1792  * @see {@link module:uloop#interval|interval()}
1793  *
1794  * @example
1795  *
1796  * const intv = uloop.interval(…);
1797  *
1798  * intv.set(…);
1799  * intv.remaining();
1800  * intv.expirations();
1801  * intv.cancel();
1802  */
1803 #ifdef HAVE_ULOOP_INTERVAL
1804 typedef struct {
1805         uc_uloop_cb_t cb;
1806         struct uloop_interval interval;
1807 } uc_uloop_interval_t;
1808 
1809 static int
1810 uc_uloop_interval_clear(uc_uloop_interval_t *interval)
1811 {
1812         int rv = uloop_interval_cancel(&interval->interval);
1813 
1814         uc_uloop_cb_free(&interval->cb);
1815 
1816         return rv;
1817 }
1818 
1819 /**
1820  * Rearms the uloop interval timer with the specified interval.
1821  *
1822  * This method rearms the interval timer with the specified interval value,
1823  * allowing it to trigger repeatedly after the specified amount of time. If no
1824  * interval value is provided or if the provided value is negative, the interval
1825  * remains disabled until rearmed with a positive interval value.
1826  *
1827  * @function module:uloop.interval#set
1828  *
1829  * @param {number} [interval=-1]
1830  * Optional. The interval value in milliseconds specifying when the interval
1831  * triggers again. Defaults to -1, which disables the interval until rearmed
1832  * with a positive interval value.
1833  *
1834  * @returns {?boolean}
1835  * Returns `true` on success, `null` on error, such as an invalid interval argument.
1836  *
1837  * @example
1838  * // Rearm the uloop interval with a interval of 1000 milliseconds
1839  * const success = interval.set(1000);
1840  *
1841  * if (success)
1842  *     printf("Interval rearmed successfully\n");
1843  * else
1844  *     printf("Error occurred while rearming interval: ${uloop.error()}\n");
1845  *
1846  * // Disable the uloop interval
1847  * const success = interval.set();
1848  *
1849  * if (success)
1850  *     printf("Interval disabled successfully\n");
1851  * else
1852  *     printf("Error occurred while disabling interval: ${uloop.error()}\n");
1853  */
1854 static uc_value_t *
1855 uc_uloop_interval_set(uc_vm_t *vm, size_t nargs)
1856 {
1857         uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval");
1858         uc_value_t *timeout = uc_fn_arg(0);
1859         int t, rv;
1860 
1861         if (!interval)
1862                 err_return(EINVAL);
1863 
1864         errno = 0;
1865         t = timeout ? (int)ucv_int64_get(timeout) : -1;
1866 
1867         if (errno)
1868                 err_return(errno);
1869 
1870         rv = uloop_interval_set(&interval->interval, t);
1871 
1872         ok_return(ucv_boolean_new(rv == 0));
1873 }
1874 
1875 /**
1876  * Returns the milliseconds until the next expiration.
1877  *
1878  * This method returns the remaining time until the uloop interval expires
1879  * and triggers again. If the interval is not armed (i.e., disabled),
1880  * it returns -1.
1881  *
1882  * @function module:uloop.interval#remaining
1883  *
1884  * @returns {number}
1885  * The milliseconds until the next expiration of the uloop interval, or -1 if
1886  * the interval is not armed.
1887  *
1888  * @example
1889  * // Get the milliseconds until the next expiration of the uloop interval
1890  * const remainingTime = interval.remaining();
1891  *
1892  * if (remainingTime !== -1)
1893  *     printf("Milliseconds until next expiration: %d\n", remainingTime);
1894  * else
1895  *     printf("Interval is not armed\n");
1896  */
1897 static uc_value_t *
1898 uc_uloop_interval_remaining(uc_vm_t *vm, size_t nargs)
1899 {
1900         uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval");
1901 
1902         if (!interval)
1903                 err_return(EINVAL);
1904 
1905         ok_return(ucv_int64_new(uloop_interval_remaining(&interval->interval)));
1906 }
1907 
1908 /**
1909  * Returns number of times the interval timer fired.
1910  *
1911  * This method returns the number of times the uloop interval timer has expired
1912  * (fired) since it was instantiated.
1913  *
1914  * @function module:uloop.interval#expirations
1915  *
1916  * @returns {number}
1917  * The number of times the uloop interval timer has expired (fired).
1918  *
1919  * @example
1920  * // Get the number of times the uloop interval timer has expired
1921  * const expirations = interval.expirations();
1922  * printf("Number of expirations: %d\n", expirations);
1923  */
1924 static uc_value_t *
1925 uc_uloop_interval_expirations(uc_vm_t *vm, size_t nargs)
1926 {
1927         uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval");
1928 
1929         if (!interval)
1930                 err_return(EINVAL);
1931 
1932         ok_return(ucv_int64_new(interval->interval.expirations));
1933 }
1934 
1935 /**
1936  * Cancels the uloop interval.
1937  *
1938  * This method cancels the uloop interval, disarming it and removing it from the
1939  * event loop. Associated resources are released.
1940  *
1941  * @function module:uloop.interval#cancel
1942  *
1943  * @returns {boolean}
1944  * Returns `true` on success.
1945  *
1946  * @example
1947  * // Cancel the uloop interval
1948  * interval.cancel();
1949  */
1950 static uc_value_t *
1951 uc_uloop_interval_cancel(uc_vm_t *vm, size_t nargs)
1952 {
1953         uc_uloop_interval_t *interval = uc_fn_thisval("uloop.interval");
1954         int rv;
1955 
1956         if (!interval)
1957                 err_return(EINVAL);
1958 
1959         rv = uc_uloop_interval_clear(interval);
1960 
1961         ok_return(ucv_boolean_new(rv == 0));
1962 }
1963 
1964 static void
1965 uc_uloop_interval_cb(struct uloop_interval *uintv)
1966 {
1967         uc_uloop_interval_t *interval = container_of(uintv, uc_uloop_interval_t, interval);
1968 
1969         uc_uloop_cb_invoke(&interval->cb, NULL, 0);
1970 }
1971 
1972 /**
1973  * Creates an interval instance for scheduling repeated callbacks.
1974  *
1975  * This function creates an interval instance for scheduling repeated callbacks
1976  * to be executed at regular intervals. It takes an optional timeout parameter,
1977  * which defaults to -1, indicating that the interval is initially not armed
1978  * and can be armed later with the `.set(timeout)` method. A callback function
1979  * must be provided to be executed when the interval expires.
1980  *
1981  * @function module:uloop#interval
1982  *
1983  * @param {number} [timeout=-1]
1984  * Optional. The interval duration in milliseconds. Defaults to -1, indicating
1985  * the interval is not initially armed.
1986  *
1987  * @param {Function} callback
1988  * The callback function to be executed when the interval expires.
1989  *
1990  * @returns {?module:uloop.interval}
1991  * Returns an interval instance for scheduling repeated callbacks.
1992  * Returns `null` when the timeout or callback arguments are invalid.
1993  *
1994  * @example
1995  * // Create an interval with a callback to be executed every 1000 milliseconds
1996  * const myInterval = uloop.interval(1000, () => {
1997  *     printf("Interval callback executed!\n");
1998  * });
1999  *
2000  * // Later arm the interval to start executing the callback every 500 milliseconds
2001  * myInterval.set(500);
2002  */
2003 static uc_value_t *
2004 uc_uloop_interval(uc_vm_t *vm, size_t nargs)
2005 {
2006         uc_value_t *timeout = uc_fn_arg(0);
2007         uc_value_t *callback = uc_fn_arg(1);
2008         uc_uloop_interval_t *interval;
2009         int t;
2010 
2011         errno = 0;
2012         t = timeout ? ucv_int64_get(timeout) : -1;
2013 
2014         if (errno)
2015                 err_return(errno);
2016 
2017         if (!ucv_is_callable(callback))
2018                 err_return(EINVAL);
2019 
2020         interval = uc_uloop_alloc(vm, "uloop.interval", sizeof(*interval), callback);
2021         interval->interval.cb = uc_uloop_interval_cb;
2022         if (t >= 0)
2023                 uloop_interval_set(&interval->interval, t);
2024 
2025         ok_return(interval->cb.obj);
2026 }
2027 #endif
2028 
2029 
2030 /**
2031  * Represents a uloop signal Unix process signal handler as returned by
2032  * {@link module:uloop#signal|signal()}.
2033  *
2034  * @class module:uloop.signal
2035  * @hideconstructor
2036  *
2037  * @see {@link module:uloop#signal|signal()}
2038  *
2039  * @example
2040  *
2041  * const sighandler = uloop.signal(…);
2042  *
2043  * sighandler.signo();
2044  * sighandler.delete();
2045  */
2046 #ifdef HAVE_ULOOP_SIGNAL
2047 typedef struct {
2048         uc_uloop_cb_t cb;
2049         struct uloop_signal signal;
2050 } uc_uloop_signal_t;
2051 
2052 static int
2053 uc_uloop_signal_clear(uc_uloop_signal_t *signal)
2054 {
2055         int rv = uloop_signal_delete(&signal->signal);
2056 
2057         uc_uloop_cb_free(&signal->cb);
2058 
2059         return rv;
2060 }
2061 
2062 /**
2063  * Returns the associated signal number.
2064  *
2065  * This method returns the signal number that this uloop signal handler is
2066  * configured to respond to.
2067  *
2068  * @function module:uloop.signal#signo
2069  *
2070  * @returns {number}
2071  * The signal number that this handler is responding to.
2072  *
2073  * @example
2074  * // Get the signal number that the uloop signal handler is responding to
2075  * const sighandler = uloop.signal("SIGINT", () => printf("Cought INT\n"));
2076  * printf("Signal number: %d\n", sighandler.signo());
2077  */
2078 static uc_value_t *
2079 uc_uloop_signal_signo(uc_vm_t *vm, size_t nargs)
2080 {
2081         uc_uloop_signal_t *signal = uc_fn_thisval("uloop.signal");
2082 
2083         if (!signal)
2084                 err_return(EINVAL);
2085 
2086         ok_return(ucv_int64_new(signal->signal.signo));
2087 }
2088 
2089 /**
2090  * Uninstalls the signal handler.
2091  *
2092  * This method uninstalls the signal handler, restoring the previous or default
2093  * handler for the signal, and releasing any associated resources.
2094  *
2095  * @function module:uloop.signal#delete
2096  *
2097  * @returns {boolean}
2098  * Returns `true` on success.
2099  *
2100  * @example
2101  * // Uninstall the signal handler and restore the previous/default handler
2102  * const sighandler = uloop.signal(…);
2103  * sighandler.delete();
2104  */
2105 static uc_value_t *
2106 uc_uloop_signal_delete(uc_vm_t *vm, size_t nargs)
2107 {
2108         uc_uloop_signal_t *signal = uc_fn_thisval("uloop.signal");
2109         int rv;
2110 
2111         if (!signal)
2112                 err_return(EINVAL);
2113 
2114         rv = uc_uloop_signal_clear(signal);
2115 
2116         if (rv != 0)
2117                 err_return(EINVAL);
2118 
2119         ok_return(ucv_boolean_new(true));
2120 }
2121 
2122 static void
2123 uc_uloop_signal_cb(struct uloop_signal *usig)
2124 {
2125         uc_uloop_signal_t *signal = container_of(usig, uc_uloop_signal_t, signal);
2126 
2127         uc_uloop_cb_invoke(&signal->cb, NULL, 0);
2128 }
2129 
2130 static int
2131 parse_signo(uc_value_t *sigspec)
2132 {
2133         if (ucv_type(sigspec) == UC_STRING) {
2134                 const char *signame = ucv_string_get(sigspec);
2135 
2136                 if (!strncasecmp(signame, "SIG", 3))
2137                         signame += 3;
2138 
2139                 for (size_t i = 0; i < UC_SYSTEM_SIGNAL_COUNT; i++) {
2140                         if (!uc_system_signal_names[i])
2141                                 continue;
2142 
2143                         if (strcasecmp(uc_system_signal_names[i], signame))
2144                                 continue;
2145 
2146                         return i;
2147                 }
2148         }
2149 
2150         uc_value_t *signum = ucv_to_number(sigspec);
2151         int64_t signo = ucv_int64_get(signum);
2152         ucv_put(signum);
2153 
2154         if (signo < 1 || signo >= UC_SYSTEM_SIGNAL_COUNT)
2155                 return -1;
2156 
2157         return signo;
2158 }
2159 
2160 /**
2161  * Creates a signal instance for handling Unix signals.
2162  *
2163  * This function creates a signal instance for handling Unix signals.
2164  * It takes the signal name string (with or without "SIG" prefix) or signal
2165  * number, and a callback function to be invoked when the specified Unix signal
2166  * is caught.
2167  *
2168  * @function module:uloop#signal
2169  *
2170  * @param {string|number} signal
2171  * The signal name string (with or without "SIG" prefix) or signal number.
2172  *
2173  * @param {Function} callback
2174  * The callback function to be invoked when the specified Unix signal is caught.
2175  *
2176  * @returns {?module:uloop.signal}
2177  * Returns a signal instance representing the installed signal handler.
2178  * Returns `null` when the signal or callback arguments are invalid.
2179  *
2180  * @example
2181  * // Create a signal instance for handling SIGINT
2182  * const mySignal = uloop.signal("SIGINT", () => {
2183  *     printf("SIGINT caught!\n");
2184  * });
2185  */
2186 static uc_value_t *
2187 uc_uloop_signal(uc_vm_t *vm, size_t nargs)
2188 {
2189         int signo = parse_signo(uc_fn_arg(0));
2190         uc_value_t *callback = uc_fn_arg(1);
2191         uc_uloop_signal_t *signal;
2192 
2193         if (signo == -1 || !ucv_is_callable(callback))
2194                 err_return(EINVAL);
2195 
2196         signal = uc_uloop_alloc(vm, "uloop.signal", sizeof(*signal), callback);
2197         signal->signal.signo = signo;
2198         signal->signal.cb = uc_uloop_signal_cb;
2199 
2200         uloop_signal_add(&signal->signal);
2201 
2202         ok_return(signal->cb.obj);
2203 }
2204 #endif
2205 
2206 static uc_value_t *
2207 uc_uloop_guard(uc_vm_t *vm, size_t nargs)
2208 {
2209         uc_value_t *arg = uc_fn_arg(0);
2210 
2211         if (!nargs)
2212                 return ucv_get(uc_vm_registry_get(vm, "uloop.ex_handler"));
2213 
2214         if (arg && !ucv_is_callable(arg))
2215                 return NULL;
2216 
2217         uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg));
2218 
2219         return ucv_boolean_new(true);
2220 }
2221 
2222 
2223 static const uc_function_list_t timer_fns[] = {
2224         { "set",                uc_uloop_timer_set },
2225         { "remaining",  uc_uloop_timer_remaining },
2226         { "cancel",             uc_uloop_timer_cancel },
2227 };
2228 
2229 static const uc_function_list_t handle_fns[] = {
2230         { "fileno",             uc_uloop_handle_fileno },
2231         { "handle",             uc_uloop_handle_handle },
2232         { "delete",             uc_uloop_handle_delete },
2233 };
2234 
2235 static const uc_function_list_t process_fns[] = {
2236         { "pid",                uc_uloop_process_pid },
2237         { "delete",             uc_uloop_process_delete },
2238 };
2239 
2240 static const uc_function_list_t task_fns[] = {
2241         { "pid",                uc_uloop_task_pid },
2242         { "kill",               uc_uloop_task_kill },
2243         { "finished",   uc_uloop_task_finished },
2244 };
2245 
2246 static const uc_function_list_t pipe_fns[] = {
2247         { "send",               uc_uloop_pipe_send },
2248         { "receive",    uc_uloop_pipe_receive },
2249         { "sending",    uc_uloop_pipe_sending },
2250         { "receiving",  uc_uloop_pipe_receiving },
2251 };
2252 
2253 #ifdef HAVE_ULOOP_INTERVAL
2254 static const uc_function_list_t interval_fns[] = {
2255         { "set",                uc_uloop_interval_set },
2256         { "remaining",  uc_uloop_interval_remaining },
2257         { "expirations",
2258                                         uc_uloop_interval_expirations },
2259         { "cancel",             uc_uloop_interval_cancel },
2260 };
2261 #endif
2262 
2263 #ifdef HAVE_ULOOP_SIGNAL
2264 static const uc_function_list_t signal_fns[] = {
2265         { "signo",              uc_uloop_signal_signo },
2266         { "delete",             uc_uloop_signal_delete },
2267 };
2268 #endif
2269 
2270 static const uc_function_list_t global_fns[] = {
2271         { "error",              uc_uloop_error },
2272         { "init",               uc_uloop_init },
2273         { "run",                uc_uloop_run },
2274         { "timer",              uc_uloop_timer },
2275         { "handle",             uc_uloop_handle },
2276         { "process",    uc_uloop_process },
2277         { "task",               uc_uloop_task },
2278         { "cancelling", uc_uloop_cancelling },
2279         { "running",    uc_uloop_running },
2280         { "done",               uc_uloop_done },
2281         { "end",                uc_uloop_end },
2282 #ifdef HAVE_ULOOP_INTERVAL
2283         { "interval",   uc_uloop_interval },
2284 #endif
2285 #ifdef HAVE_ULOOP_SIGNAL
2286         { "signal",             uc_uloop_signal },
2287 #endif
2288         { "guard",              uc_uloop_guard },
2289 };
2290 
2291 
2292 static void close_timer(void *ud)
2293 {
2294         uc_uloop_timeout_clear(ud);
2295 }
2296 
2297 static void close_handle(void *ud)
2298 {
2299         uc_uloop_handle_clear(ud);
2300 }
2301 
2302 static void close_process(void *ud)
2303 {
2304         uc_uloop_process_clear(ud);
2305 }
2306 
2307 static void close_task(void *ud)
2308 {
2309         uc_uloop_task_clear(ud);
2310 }
2311 
2312 static void close_pipe(void *ud)
2313 {
2314         uc_uloop_pipe_t *pipe = ud;
2315 
2316         if (!pipe)
2317                 return;
2318 
2319         close(pipe->input);
2320         close(pipe->output);
2321 
2322         free(pipe);
2323 }
2324 
2325 #ifdef HAVE_ULOOP_INTERVAL
2326 static void close_interval(void *ud)
2327 {
2328         uc_uloop_interval_clear(ud);
2329 }
2330 #endif
2331 
2332 #ifdef HAVE_ULOOP_SIGNAL
2333 static void close_signal(void *ud)
2334 {
2335         uc_uloop_signal_clear(ud);
2336 }
2337 #endif
2338 
2339 
2340 static struct {
2341         struct uloop_fd ufd;
2342         uc_vm_t *vm;
2343 } signal_handle;
2344 
2345 static void
2346 uc_uloop_vm_signal_cb(struct uloop_fd *ufd, unsigned int events)
2347 {
2348         if (uc_vm_signal_dispatch(signal_handle.vm) != EXCEPTION_NONE)
2349                 uloop_end();
2350 }
2351 
2352 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
2353 {
2354         int signal_fd;
2355 
2356         uc_function_list_register(scope, global_fns);
2357 
2358 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
2359 
2360         /**
2361          * @typedef
2362          * @name Event Mode Constants
2363          * @description
2364          * The `ULOOP_*` constants are passed as bitwise OR-ed number to the
2365          * {@link module:uloop.handle#handle|handle()} function to specify the IO
2366          * events that should be monitored on the given handle.
2367          * @property {number} ULOOP_READ - File or socket is readable.
2368          * @property {number} ULOOP_WRITE - File or socket is writable.
2369          * @property {number} ULOOP_EDGE_TRIGGER - Enable edge-triggered event mode.
2370          * @property {number} ULOOP_BLOCKING - Do not make descriptor non-blocking.
2371          */
2372         ADD_CONST(ULOOP_READ);
2373         ADD_CONST(ULOOP_WRITE);
2374         ADD_CONST(ULOOP_EDGE_TRIGGER);
2375         ADD_CONST(ULOOP_BLOCKING);
2376 
2377         uc_type_declare(vm, "uloop.timer", timer_fns, close_timer);
2378         uc_type_declare(vm, "uloop.handle", handle_fns, close_handle);
2379         uc_type_declare(vm, "uloop.process", process_fns, close_process);
2380         uc_type_declare(vm, "uloop.task", task_fns, close_task);
2381         uc_type_declare(vm, "uloop.pipe", pipe_fns, close_pipe);
2382 
2383 #ifdef HAVE_ULOOP_INTERVAL
2384         uc_type_declare(vm, "uloop.interval", interval_fns, close_interval);
2385 #endif
2386 
2387 #ifdef HAVE_ULOOP_SIGNAL
2388         uc_type_declare(vm, "uloop.signal", signal_fns, close_signal);
2389 #endif
2390 
2391         signal_fd = uc_vm_signal_notifyfd(vm);
2392 
2393         if (signal_fd != -1 && uloop_init() == 0) {
2394                 signal_handle.vm = vm;
2395                 signal_handle.ufd.cb = uc_uloop_vm_signal_cb;
2396                 signal_handle.ufd.fd = signal_fd;
2397 
2398                 uloop_fd_add(&signal_handle.ufd, ULOOP_READ);
2399         }
2400 }
2401 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt