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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt