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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt