diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/ioloop.h | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/src/lib/ioloop.h b/src/lib/ioloop.h new file mode 100644 index 0000000..32e2892 --- /dev/null +++ b/src/lib/ioloop.h @@ -0,0 +1,323 @@ +#ifndef IOLOOP_H +#define IOLOOP_H + +#include <sys/time.h> +#include <time.h> + +struct io; +struct timeout; +struct ioloop; +struct istream; + +enum io_condition { + IO_READ = 0x01, + IO_WRITE = 0x02, + /* IO_ERROR can be used to check when writable pipe's reader side + closes the pipe. For other uses IO_READ should work just as well. */ + IO_ERROR = 0x04, + + /* internal */ + IO_NOTIFY = 0x08 +}; + +enum io_notify_result { + /* Notify added successfully */ + IO_NOTIFY_ADDED, + /* Specified file doesn't exist, can't wait on it */ + IO_NOTIFY_NOTFOUND, + /* Can't add notify for specified file. Main reasons for this: + a) No notify support at all, b) Only directory notifies supported */ + IO_NOTIFY_NOSUPPORT +}; + +typedef void io_callback_t(void *context); +typedef void timeout_callback_t(void *context); +typedef void io_loop_time_moved_callback_t(const struct timeval *old_time, + const struct timeval *new_time); +typedef void io_switch_callback_t(struct ioloop *prev_ioloop); +typedef void io_destroy_callback_t(struct ioloop *ioloop); + +/* Time when the I/O loop started calling handlers. + Can be used instead of time(NULL). */ +extern time_t ioloop_time; +extern struct timeval ioloop_timeval; + +extern struct ioloop *current_ioloop; +/* Number of microseconds spent on all the ioloops waiting for themselves. */ +extern uint64_t ioloop_global_wait_usecs; + +/* You can create different handlers for IO_READ and IO_WRITE. IO_READ and + IO_ERROR can't use different handlers (and there's no point anyway). + + Don't try to add multiple handlers for the same type. It's not checked and + the behavior will be undefined. */ +struct io *io_add(int fd, enum io_condition condition, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) ATTR_NULL(5); +#define io_add(fd, condition, callback, context) \ + io_add(fd, condition, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct io *io_add_to(struct ioloop *ioloop, int fd, enum io_condition condition, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) ATTR_NULL(5); +#define io_add_to(ioloop, fd, condition, callback, context) \ + io_add_to(ioloop, fd, condition, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +enum io_notify_result +io_add_notify(const char *path, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context, + struct io **io_r) ATTR_NULL(3); +#define io_add_notify(path, callback, context, io_r) \ + io_add_notify(path, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context, io_r) + +struct io *io_add_istream(struct istream *input, const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) ATTR_NULL(3); +#define io_add_istream(input, callback, context) \ + io_add_istream(input, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct io *io_add_istream_to(struct ioloop *ioloop, struct istream *input, + const char *source_filename, + unsigned int source_linenum, + io_callback_t *callback, void *context) + ATTR_NULL(3); +#define io_add_istream_to(ioloop, input, callback, context) \ + io_add_istream_to(ioloop, input, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +/* Remove I/O handler, and set io pointer to NULL. */ +void io_remove(struct io **io); +/* Like io_remove(), but assume that the file descriptor is already closed. + With some backends this simply frees the memory. */ +void io_remove_closed(struct io **io); + +/* Make sure the I/O callback is called by io_loop_run() even if there isn't + any input actually pending currently as seen by the OS. This may be useful + if some of the input has already read into some internal buffer and the + caller wants to handle it the same way as if the fd itself had input. */ +void io_set_pending(struct io *io); +/* Returns TRUE if io_set_pending() has been called for the IO and its callback + hasn't been called yet. */ +bool io_is_pending(struct io *io); +/* If set, this IO shouldn't be the only thing being waited on, because + it would just result in infinite wait. In those situations rather just + crash to indicate that there's a bug. */ +void io_set_never_wait_alone(struct io *io, bool set); + +/* Timeout handlers */ +struct timeout * +timeout_add(unsigned int msecs, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add(msecs, callback, context) \ + timeout_add(msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))) - \ + COMPILE_ERROR_IF_TRUE(__builtin_constant_p(msecs) && \ + ((msecs) > 0 && (msecs) < 1000)), \ + (io_callback_t *)callback, context) +struct timeout * +timeout_add_to(struct ioloop *ioloop, unsigned int msecs, + const char *source_filename, unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_to(ioloop, msecs, callback, context) \ + timeout_add_to(ioloop, msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))) - \ + COMPILE_ERROR_IF_TRUE(__builtin_constant_p(msecs) && \ + ((msecs) > 0 && (msecs) < 1000)), \ + (io_callback_t *)callback, context) + +struct timeout * +timeout_add_short(unsigned int msecs, const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_short(msecs, callback, context) \ + timeout_add_short(msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct timeout * +timeout_add_short_to(struct ioloop *ioloop, unsigned int msecs, + const char *source_filename, unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_short_to(ioloop, msecs, callback, context) \ + timeout_add_short_to(ioloop, msecs, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +struct timeout * +timeout_add_absolute(const struct timeval *time, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_absolute(time, callback, context) \ + timeout_add_absolute(time, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) +struct timeout * +timeout_add_absolute_to(struct ioloop *ioloop, + const struct timeval *time, + const char *source_filename, + unsigned int source_linenum, + timeout_callback_t *callback, void *context) ATTR_NULL(4); +#define timeout_add_absolute_to(ioloop, time, callback, context) \ + timeout_add_absolute_to(ioloop, time, __FILE__, __LINE__ - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \ + (io_callback_t *)callback, context) + +/* Remove timeout handler, and set timeout pointer to NULL. */ +void timeout_remove(struct timeout **timeout); +/* Reset timeout so it's next run after now+msecs. */ +void timeout_reset(struct timeout *timeout); + +/* Refresh ioloop_time and ioloop_timeval variables. */ +void io_loop_time_refresh(void); + +void io_loop_run(struct ioloop *ioloop); +/* Stop the ioloop immediately. No further IO or timeout callbacks are called. + Warning: This is not safe to be called in non-delayed signal handlers. */ +void io_loop_stop(struct ioloop *ioloop); +/* Stop ioloop after finishing all the pending IOs and timeouts. */ +void io_loop_stop_delayed(struct ioloop *ioloop); + +bool io_loop_is_running(struct ioloop *ioloop); + +/* call these if you wish to run the iteration only once */ +void io_loop_set_running(struct ioloop *ioloop); +void io_loop_handler_run(struct ioloop *ioloop); + +struct ioloop *io_loop_create(void); +/* Specify the maximum number of fds we're expecting to use. */ +void io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds); +/* Destroy I/O loop and set ioloop pointer to NULL. */ +void io_loop_destroy(struct ioloop **ioloop); + +/* If time moves backwards or jumps forwards call the callback. */ +void io_loop_set_time_moved_callback(struct ioloop *ioloop, + io_loop_time_moved_callback_t *callback); + +/* Change the current_ioloop. */ +void io_loop_set_current(struct ioloop *ioloop); +/* Return the root ioloop. */ +struct ioloop *io_loop_get_root(void); +/* Call the callback whenever ioloop is changed. */ +void io_loop_add_switch_callback(io_switch_callback_t *callback); +void io_loop_remove_switch_callback(io_switch_callback_t *callback); +/* Call the callback whenever ioloop is destroyed. */ +void io_loop_add_destroy_callback(io_destroy_callback_t *callback); +void io_loop_remove_destroy_callback(io_destroy_callback_t *callback); + +/* Create a new ioloop context. While the context is activated, it's + automatically attached to all the following I/Os and timeouts that are + added until the context is deactivated (e.g. returning to back to a running + ioloop). Whenever such added I/O or timeout callback is called, this context + is automatically activated. + + After the context is created, callbacks should be added to it and the + context should be activated with either io_loop_context_activate() or + io_loop_context_switch(). */ +struct ioloop_context *io_loop_context_new(struct ioloop *ioloop); +void io_loop_context_ref(struct ioloop_context *ctx); +void io_loop_context_unref(struct ioloop_context **ctx); +/* Call the activate callback when this context is activated (I/O callback is + about to be called), and the deactivate callback when the context is + deactivated (I/O callback has returned). You can add multiple callbacks. + + The ioloop context is a global state, so only a single context can be active + at a time. The callbacks are guaranteed to be called only at their proper + states, i.e. activate() callback is called only when switching from + no context to the active context, and deactive() is called only when + switching from previously activated context into no context. No context is + active at a time when the ioloop is destroyed. */ +void io_loop_context_add_callbacks(struct ioloop_context *ctx, + io_callback_t *activate, + io_callback_t *deactivate, void *context); +#define io_loop_context_add_callbacks(ctx, activate, deactivate, context) \ + io_loop_context_add_callbacks(ctx, 1 ? (io_callback_t *)activate : \ + CALLBACK_TYPECHECK(activate, void (*)(typeof(context))) - \ + CALLBACK_TYPECHECK(deactivate, void (*)(typeof(context))), \ + (io_callback_t *)deactivate, context) +/* Remove callbacks with the given callbacks and context. */ +void io_loop_context_remove_callbacks(struct ioloop_context *ctx, + io_callback_t *activate, + io_callback_t *deactivate, void *context); +#define io_loop_context_remove_callbacks(ctx, activate, deactivate, context) \ + io_loop_context_remove_callbacks(ctx, 1 ? (io_callback_t *)activate : \ + CALLBACK_TYPECHECK(activate, void (*)(typeof(context))) - \ + CALLBACK_TYPECHECK(deactivate, void (*)(typeof(context))), \ + (io_callback_t *)deactivate, context) +/* Returns the current context set to ioloop. */ +struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop); + +/* Explicitly activate an ioloop context. There must not be any context active + at the moment, so this most likely shouldn't be called while ioloop is + running. An activated context must be explicitly deactivated with + io_loop_context_deactivate() before the ioloop is destroyed, or before + any ioloop is run. */ +void io_loop_context_activate(struct ioloop_context *ctx); +/* Explicitly deactivate an ioloop context. The given context must be currently + active or it assert-crashes. This should be called only after a context + was explicitly activated with io_loop_context_activate(). */ +void io_loop_context_deactivate(struct ioloop_context *ctx); +/* If there's an active ioloop context, deactivate it. Then activate the given + context. Usually used after creating a new context. */ +void io_loop_context_switch(struct ioloop_context *ctx); + +/* Returns fd, which contains all of the ioloop's current notifications. + When it becomes readable, there is a new notification. Calling this function + stops the existing notifications in the ioloop from working anymore. + This function's main idea is that the fd can be passed to another process, + which can use it to find out if an interesting notification happens. + Returns fd on success, -1 on error. */ +int io_loop_extract_notify_fd(struct ioloop *ioloop); + +/* IO wait timers can be used to track how much time the io_wait_timer has + spent on waiting in its ioloops. This is similar to + io_loop_get_wait_usecs(), but it's easier to use when the wait time needs + to be tracked across multiple ioloops. */ +struct io_wait_timer * +io_wait_timer_add(const char *source_filename, unsigned int source_linenum); +#define io_wait_timer_add() \ + io_wait_timer_add(__FILE__, __LINE__) +struct io_wait_timer * +io_wait_timer_add_to(struct ioloop *ioloop, const char *source_filename, + unsigned int source_linenum); +#define io_wait_timer_add_to(ioloop) \ + io_wait_timer_add_to(ioloop, __FILE__, __LINE__) + +struct io_wait_timer *io_wait_timer_move(struct io_wait_timer **timer); +struct io_wait_timer *io_wait_timer_move_to(struct io_wait_timer **timer, + struct ioloop *ioloop); +void io_wait_timer_remove(struct io_wait_timer **timer); +uint64_t io_wait_timer_get_usecs(struct io_wait_timer *timer); + +/* Move the given I/O into the provided/current I/O loop if it's not already + there. New I/O is returned, while the old one is freed. */ +struct io *io_loop_move_io_to(struct ioloop *ioloop, struct io **_io); +struct io *io_loop_move_io(struct io **io); +/* Like io_loop_move_io(), but for timeouts. */ +struct timeout *io_loop_move_timeout_to(struct ioloop *ioloop, + struct timeout **timeout); +struct timeout *io_loop_move_timeout(struct timeout **timeout); +/* Returns TRUE if any IOs have been added to the ioloop. */ +bool io_loop_have_ios(struct ioloop *ioloop); +/* Returns TRUE if there is a pending timeout that is going to be run + immediately. */ +bool io_loop_have_immediate_timeouts(struct ioloop *ioloop); +/* Returns TRUE if there are no IOs or timeouts in the ioloop. */ +bool io_loop_is_empty(struct ioloop *ioloop); +/* Returns number of microseconds spent on the ioloop waiting itself. */ +uint64_t io_loop_get_wait_usecs(struct ioloop *ioloop); +/* Return all io conditions added for the given fd. This needs to scan through + all the file ios in the ioloop. */ +enum io_condition io_loop_find_fd_conditions(struct ioloop *ioloop, int fd); + +#endif |