summaryrefslogtreecommitdiffstats
path: root/src/lib/ioloop.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/ioloop.h323
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