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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt