/** The main loop context **/
struct main_context {
- timestamp_t now; /** [*] Current time in milliseconds since the UNIX epoch. See @main_get_time(). **/
- ucw_time_t now_seconds; /** [*] Current time in seconds since the epoch. **/
- timestamp_t idle_time; /** [*] Total time in milliseconds spent by waiting for events. **/
- uns shutdown; /** [*] Setting this to nonzero forces the @main_loop() function to terminate. **/
+ timestamp_t now; /* [*] Current time in milliseconds since the UNIX epoch. See main_get_time(). */
+ ucw_time_t now_seconds; /* [*] Current time in seconds since the epoch. */
+ timestamp_t idle_time; /* [*] Total time in milliseconds spent by waiting for events. */
+ uns shutdown; /* [*] Setting this to nonzero forces the main_loop() function to terminate. */
clist file_list;
clist file_active_list;
clist hook_list;
clist process_list;
clist signal_list;
uns file_cnt;
+ uns single_step;
#ifdef CONFIG_UCW_EPOLL
int epoll_fd; /* File descriptor used for epoll */
struct epoll_event *epoll_events;
/**
* Start the event loop on the current context.
* It will watch the provided objects and call callbacks.
- * Terminates when someone sets <<var_main_shutdown,`main_shutdown`>>
- * to nonzero, when all <<hook,hooks>> return
- * <<enum_main_hook_return,`HOOK_DONE`>> or at last one <<hook,hook>>
- * returns <<enum_main_hook_return,`HOOK_SHUTDOWN`>>.
+ * Terminates when someone calls @main_shut_down(),
+ * or when all <<hook,hooks>> return <<enum_main_hook_return,`HOOK_DONE`>>
+ * or at last one <<hook,hook>> returns <<enum_main_hook_return,`HOOK_SHUTDOWN`>>.
**/
void main_loop(void);
+/**
+ * Perform a single iteration of the main loop.
+ * Check if there are any events ready and process them.
+ * If there are none, do not wait.
+ **/
+void main_step(void);
+
/** Ask the main loop to terminate at the nearest occasion. **/
static inline void main_shut_down(void)
{
main_current()->shutdown = 1;
}
-/** Show the current state of a given context (use @main_debug() for the current context). **/
+/**
+ * Show the current state of a given context (use @main_debug() for the current context).
+ * Available only if LibUCW has been compiled with `CONFIG_DEBUG`.
+ **/
void main_debug_context(struct main_context *m);
static inline void
**/
void main_get_time(void);
+/** Show current state of a timer. Available only if LibUCW has been compiled with `CONFIG_DEBUG`. **/
+void timer_debug(struct main_timer *tm);
+
/***
* [[file]]
* Activity on file descriptors
* From within the handler functions, you are allowed to call @file_chg() and even
* @file_del().
*
- * The return value of a handler function should be either `HOOK_RETRY` or `HOOK_IDLE`.
- * `HOOK_RETRY` signals that the function would like to consume more data immediately
+ * The return value of a handler function should be either <<enum_main_hook_return,`HOOK_RETRY`>>
+ * or <<enum_main_hook_return,`HOOK_IDLE`>>. <<enum_main_hook_return,`HOOK_RETRY`>>
+ * signals that the function would like to consume more data immediately
* (i.e., it wants to be called again soon, but the event loop can postpone it after
- * processing other events to avoid starvation). `HOOK_IDLE` tells that the handler
- * wants to be called when the descriptor becomes ready again.
+ * processing other events to avoid starvation). <<enum_main_hook_return,`HOOK_IDLE`>>
+ * tells that the handler wants to be called when the descriptor becomes ready again.
*
- * For backward compatibility, 0 can be used instead of `HOOK_IDLE` and 1 for `HOOK_RETRY`.
+ * For backward compatibility, 0 can be used instead of <<enum_main_hook_return,`HOOK_IDLE`>>
+ * and 1 for <<enum_main_hook_return,`HOOK_RETRY`>>.
*
* If you want to read/write fixed-size blocks of data asynchronously, the
* <<blockio,Asynchronous block I/O>> interface could be more convenient.
* The file descriptor is automatically set to the non-blocking mode.
**/
void file_add(struct main_file *fi);
+
/**
* Tell the main loop that the file structure has changed. Call it whenever you
* change any of the handlers.
* Can be called only on active files (only the ones added by @file_add()).
**/
void file_chg(struct main_file *fi);
+
/**
* Removes a file from the watched set. If you want to close a descriptor,
* please use this function first.
**/
void file_del(struct main_file *fi);
+/** Show current state of a file. Available only if LibUCW has been compiled with `CONFIG_DEBUG`. **/
+void file_debug(struct main_file *fi);
+
/***
* [[blockio]]
* Asynchronous block I/O
* and writes for you.
*
* You just create <<struct_main_block_io,`struct main_block_io`>> and call
- * @block_io_add() on it, which sets up some `main_file`s internally.
+ * @block_io_add() on it, which sets up some <<struct_main_file,`main_file`>>s internally.
* Then you can just call @block_io_read() or @block_io_write() to ask for
* reading or writing of a given block. When the operation is finished,
* your handler function is called.
* (except that it gets added and deleted at the right places), feel free
* to adjust it from your handler functions by @block_io_set_timeout().
* When the timer expires, the error handler is automatically called with
- * `BIO_ERR_TIMEOUT`.
+ * <<enum_block_io_err_cause,`BIO_ERR_TIMEOUT`>>.
***/
/** The block I/O structure. **/
* This will cause calling of all the hooks again soon.
* - `HOOK_DONE` -- The loop will terminate if all hooks return this.
* - `HOOK_SHUTDOWN` -- Shuts down the loop.
+ *
+ * The `HOOK_IDLE` and `HOOK_RETRY` constants are also used as return values
+ * of file handlers.
**/
enum main_hook_return {
HOOK_IDLE,
* May be called from inside a hook handler too.
**/
void hook_add(struct main_hook *ho);
+
/**
* Removes an existing hook from the loop.
- * May be called from inside a hook handler (to delete itself or other hook).
+ * May be called from inside a hook handler (to delete itself or another hook).
**/
void hook_del(struct main_hook *ho);
+/** Show current state of a hook. Available only if LibUCW has been compiled with `CONFIG_DEBUG`. **/
+void hook_debug(struct main_hook *ho);
+
/***
* [[process]]
* Child processes
* if you removed the process previously by @process_del().
**/
void process_add(struct main_process *mp);
+
/**
* Removes the process from the watched set. This is done
* automatically, when the process terminates, so you need it only
* when you do not want to watch a running process any more.
*/
void process_del(struct main_process *mp);
+
/**
* Forks and fills the @mp with information about the new process.
*
**/
int process_fork(struct main_process *mp);
-/* FIXME: Docs */
+/** Show current state of a process. Available only if LibUCW has been compiled with `CONFIG_DEBUG`. **/
+void process_debug(struct main_process *pr);
+
+/***
+ * [[signal]]
+ * Synchronous delivery of signals
+ * -------------------------------
+ *
+ * UNIX signals are delivered to processes in an asynchronous way: when a signal
+ * arrives (and it is not blocked), the process is interrupted and the corresponding
+ * signal handler function is called. However, most data structures and even most
+ * system library calls are not safe with respect to interrupts, so most program
+ * using signals contain subtle race conditions and may fail once in a long while.
+ *
+ * To avoid this problem, the event loop can be asked for synchronous delivery
+ * of signals. When a signal registered with @signal_add() arrives, it wakes up
+ * the loop (if it is not already awake) and it is processed in the same way
+ * as all other events.
+ *
+ * When used in a multi-threaded program, the signals are delivered to the thread
+ * which is currently using the particular main loop context. If the context is not
+ * current in any thread, the signals are blocked.
+ *
+ * As usually with UNIX signals, multiple instances of a single signal can be
+ * merged and delivered only once. (Some implementations of the main loop can even
+ * drop a signal completely during very intensive signal traffic, when an internal
+ * signal queue overflows.)
+ ***/
+/** Description of a signal to catch. **/
struct main_signal {
cnode n;
- int signum;
- void (*handler)(struct main_signal *ms);
- void *data;
+ int signum; /* [*] Signal to catch */
+ void (*handler)(struct main_signal *ms); /* [*] Called when the signal arrives */
+ void *data; /* [*] For use by the handler */
};
+/** Request a signal to be caught and delivered synchronously. **/
void signal_add(struct main_signal *ms);
+
+/** Cancel a request for signal catching. **/
void signal_del(struct main_signal *ms);
+/** Show current state of a signal catcher. Available only if LibUCW has been compiled with `CONFIG_DEBUG`. **/
+void signal_debug(struct main_signal *sg);
+
#endif