Scroll to navigation

gensio_os_funcs(3) Library Functions Manual gensio_os_funcs(3)


gensio_os_funcs - Abstraction for some operating system functions used by the gensio library


#include <gensio/gensio_os_funcs_public.h>

struct gensio_os_funcs {}

int gensio_default_os_hnd(int wake_sig, struct gensio_os_funcs **o)

int gensio_alloc_os_funcs(int wake_sig, struct gensio_os_funcs **o,
unsigned int flags, ...)

int gensio_unix_funcs_alloc(struct selector_s *sel, int wake_sig,
struct gensio_os_funcs **o)

int gensio_win_funcs_alloc(struct gensio_os_funcs **o)

void gensio_os_funcs_free(struct gensio_os_funcs *o);

int gensio_os_proc_setup(struct gensio_os_funcs *o,
struct gensio_os_proc_data **data)

void gensio_os_proc_cleanup(struct gensio_os_proc_data *data);

int gensio_os_thread_setup(struct gensio_os_funcs *o);

sigset_t *gensio_os_proc_unix_get_wait_sigset(
struct gensio_os_proc_data *data);

int gensio_os_new_thread(struct gensio_os_funcs *o,
void (*start_func)(void *data), void *data,
struct gensio_thread **thread_id);

int gensio_os_wait_thread(struct gensio_thread *thread_id);

int gensio_os_proc_register_term_handler(struct gensio_os_proc_data *data,
void (*handler)(void *handler_data),
void *handler_data);

int gensio_os_proc_register_reload_handler(struct gensio_os_proc_data *data,
void (*handler)(void *handler_data),
void *handler_data);

int gensio_os_proc_register_winsize_handler(struct gensio_os_proc_data *data,
struct gensio_iod *console_iod,
void (*handler)(int x_chrs, int y_chrs,
int x_bits, int y_bits,
void *handler_data),
void *handler_data);

void *gensio_os_funcs_zalloc(struct gensio_os_funcs *o, gensiods len);

void gensio_os_funcs_zfree(struct gensio_os_funcs *o, void *data);

struct gensio_lock *gensio_os_funcs_alloc_lock(struct gensio_os_funcs *o);

void gensio_os_funcs_free_lock(struct gensio_os_funcs *o,
struct gensio_lock *lock);

void gensio_os_funcs_lock(struct gensio_os_funcs *o,
struct gensio_lock *lock);

void gensio_os_funcs_unlock(struct gensio_os_funcs *o,
struct gensio_lock *lock);

void gensio_os_funcs_get_monotonic_time(struct gensio_os_funcs *o,
gensio_time *time);

struct gensio_timer *gensio_os_funcs_alloc_timer(struct gensio_os_funcs *o,
void (*handler)(struct gensio_timer *t,
void *cb_data),
void *cb_data);

void gensio_os_funcs_free_timer(struct gensio_os_funcs *o,
struct gensio_timer *timer);

int gensio_os_funcs_start_timer(struct gensio_os_funcs *o,
struct gensio_timer *timer,
gensio_time *timeout);

int gensio_os_funcs_start_timer_abs(struct gensio_os_funcs *o,
struct gensio_timer *timer,
gensio_time *timeout);

int gensio_os_funcs_stop_timer(struct gensio_os_funcs *o,
struct gensio_timer *timer);

int gensio_os_funcs_stop_timer_with_done(struct gensio_os_funcs *o,
struct gensio_timer *timer,
void (*done_handler)(struct gensio_timer *t,
void *cb_data),
void *cb_data);

struct gensio_runner *gensio_os_funcs_alloc_runner(struct gensio_os_funcs *o,
void (*handler)(struct gensio_runner *r,
void *cb_data),
void *cb_data);

void gensio_os_funcs_free_runner(struct gensio_os_funcs *o,
struct gensio_runner *runner);

int gensio_os_funcs_run(struct gensio_os_funcs *o,
struct gensio_runner *runner);

typedef void (gensio_vlog_func)(struct gensio_os_funcs *o,
enum gensio_log_levels level,
const char *log, va_list args);

void gensio_os_funcs_set_vlog(struct gensio_os_funcs *o,
gensio_vlog_func func);

int gensio_os_funcs_service(struct gensio_os_funcs *o, gensio_time *timeout);

int gensio_os_funcs_handle_fork(struct gensio_os_funcs *o);

struct gensio_waiter *gensio_os_funcs_alloc_waiter(struct gensio_os_funcs *o);

void gensio_os_funcs_free_waiter(struct gensio_os_funcs *o,
struct gensio_waiter *waiter);

int gensio_os_funcs_wait(struct gensio_os_funcs *o,
struct gensio_waiter *waiter, unsigned int count,
gensio_time *timeout);

int gensio_os_funcs_wait_intr(struct gensio_os_funcs *o,
struct gensio_waiter *waiter, unsigned int count,
gensio_time *timeout);

int gensio_os_funcs_wait_intr_sigmask(struct gensio_os_funcs *o,
struct gensio_waiter *waiter,
unsigned int count,
gensio_time *timeout,
struct gensio_os_proc_data *proc_data);

void gensio_os_funcs_wake(struct gensio_os_funcs *o,
struct gensio_waiter *waiter);

void gensio_os_funcs_set_data(struct gensio_os_funcs *o, void *data);

void *gensio_os_funcs_get_data(struct gensio_os_funcs *o);


This structure provides an abstraction for the gensio library that lets it work with various event libraries. It provides the following basic functions:

and report when I/O is ready on them.
straight from user functions can result in deadlocks, this provides a way to call callbacks from a separate context.
and watching for file descriptors.

This document describes the public functions that users can use. An os handler has many other functions for use by gensios, these are documented in the os funcs include file.

The basic issue is that there are various event handling libraries (Tcl/Tk, glib, Xlib, custom ones, etc.) and you may want to integrate the gensio library with one of these. Even though it's a bit of a pain to have to pass one of these around, it adds needed flexibility.

gensio_default_os_hnd (Deprecated) provides a way to get the default OS function handler for the platform. The same value will be returned each time, only one is created. You should generally use this one unless you have a special need as documented above. Use of this is now discouraged in general. Having two independent parts of a system share an OS funcs without knowing about it is likely to lead to issues. If a program knows it is the only thing using it, then this is ok for now, but it's going to be deprecated at some point.

gensio_alloc_os_funcs allocates a new OS function handler for the platform, for Unix or Windows. Multiple OS handlers can be used to handle different I/O at different priorities. When you create a gensio, all I/O callbacks will be handled from the OS handler used to create it. So you can run different OS handlers in threads of different priority to run different gensios at different priority. Otherwise there is not much reason for more than one of these.

The wake_sig parameter usage on Windows is unused. For Unix systems, this signal is used to signal other processes that may be waiting that they need to wake up. This is used to wake up a process waiting on a waiter, and it's used to signal all waiting processes if a timer is added that is sooner than any other timer so they can adjust their waits.

If you are running your program in a single thread, you can safely pass zero into this parameter. If you pass in anything but zero, it will set up that signal by removing it from the sigmask and installing a handler for it.

If your app is multi-threaded (or, more accurately, if your app has multiple threads that are making gensio calls) you must pass a valid signal into this, and you must set up an empty handler for this signal, and the signal must be blocked in all threads that call a wait function. You should not use this signal for anything else. The function that allocates a signal handler will block the signal in the calling thread, and that sigmask is passed on to other threads it creates. But if you have allocated threads before allocating the os funcs, you must make sure those other threads have this signal blocked.

You can pass in GENSIO_OS_FUNCS_DEFAULT_THREAD_SIGNAL to take the default signal handler, which is SIGUSR1.

On unix, gensio_os_proc_setup function handles all the above mentioned signal setup for you (blocking signals, setting signal handler), for the wake signal as above and also for some other signals like SIGTERM, SIGCHILD, SIGPIPE, and others. You should generally use that unless you have special needs. It also does neccessary setup on Windows.

Also, if you pass in a different value to gensio_default_os_hnd than the first one you passed in, it will return GE_INVAL. You can pass in different values to gensio_unix_funcs_alloc calls, and it will use them, but there's not much value in this. The os funcs for Unix can share a signal handler. And there's not much value is multiple OS funcs, anyway.

gensio_os_thread_setup is much like gensio_os_proc_setup, but it only sets up the signal handlers and blocking signals for the wakeup signal, it doesn't do any of the other setup. It allows you to bring a thread in to gensio that wasn't created by gensio. It also does some necessary setup on Windows for the thread. If you have no signal handling needs, you can generally use this instead of gensio_os_proc_setup(). If you have signal handling needs, one thread should call gensio_os_proc_setup() and all other threads should call this function if they were not created by gensio.

gensio_unix_funcs_alloc and gensio_win_funcs_alloc allocate the normal os funcs for Unix and Windows based systems, respectively.

The sel parameter for Unix allows you to create your own selector object and pass it to the OS handler. Passing in NULL will cause it to allocate it's own selector object. See the selector.h include file for details.

The wake_sig value is a signal for use by the OS functions for internal communication between threads. If you are running a multi-threaded application, you must provide a valid signal that you don't use for any other purpose, generally SIGUSR1 or SIGUSR2. You can use GENSIO_DEF_WAKE_SIG which is zero on Windows and SIGUSR1 on Unix.

The gensio_os_proc_setup function does all the standard setup for a process. You should almost certainly use this function. On Windows this sets up some basic things so termination registering will work, but on Unix it does all the signal handling setup, so you don't have to do all the things mentioned above. This will block SIGPIPE (because those come in when connections die and most applications don't care), SIGCHLD (those come in for stdio and pty gensios), and the wake_sig if that is set. It also install signal handlers for SIGCHLD and the wake_sig (if set) and sets up a signal mask.

For Unix this is generally what you want, you don't want SIGPIPE doing bad things and having SIGCHLD wake up a wait can speed things up a bit when waiting for subprograms.

If you use the gensio_os_funcs_wait_intr_sigmask OS function, you must pass the proc_data value returned by gensio_os_proc_setup into that. If you want to modify the wait signal mask, you can use gensio_os_proc_unix_get_wait_sigset to fetch a pointer to it and modify it.

Note that if you call this more than once without calling gensio_os_proc_cleanup inbetween, it will return GE_INUSE.

The gensio_os_proc_cleanup function undoes all the changes gensio_os_proc_setup does, along with unregistering any signal handlers done by register calls. On Unix it restores the signal mask and signal handlers it sets to their previous values. On Windows it remove registered handlers.

The gensio_os_new_thread function starts a new thread at start_func passing in the given data value. It returns a thread_id that you must pass into the wait function. This is just basic generic threads, you can use your OS functions if you need more control over the threads. If you use threads, make sure to see the notes above about setting up for them properly. This must be called from a thread that is already set up.

The gensio_os_wait_thread waits for a thread to stop. Note that it does not cause the thread to stop, it waits for it to stop. You have to cause the thread to stop yourself.

The gensio_os_proc_register_term_handler function passes a handler to call when a termination (SIGINT, SIGQUIT, SIGTERM on Unix, console control handler or WM_CLOSE on windows) is received by the process. Set handler to NULL to disable. If you do this on Unix, the signals will be blocked except when in a wait or service call. Call this before starting any other threads so they inherit the proper sigmask.

The gensio_os_proc_register_reload_handler sets the function to call when a reload is requested by the operating system (SIGHUP on Unix). Set handler to NULL to disable. On Unix, this will cause SIGHUP to be blocked except when in a wait or service call. Call this before starting any other threads so they inherit the proper sigmask.

The gensio_os_proc_register_winsize_handler function sets the function to call when a console window size change is requested by the operating system (SIGWINCH on Unix, through the console interface on Windows). It will supply the new size parameters. Set handler and console_iod to NULL to disable. Note that the handler will be called with current window size parameters after this is called. This may be called after threads are started, the SIGWINCH signal mask is set up by default in gensio_os_proc_setup on *nix systems.

gensio_os_funcs_zalloc allocates a block of memory and zeroes it. The gensio library has it's own allocator/deallocator that is using in testing to track if all allocated data is freed when the tests shut down, and to catch memory overruns, underruns, and use after free errors. Returns NULL if the memory cannot be allocated. Use gensio_os_funcs_zfree to free the allocated memory.

gensio_os_funcs_alloc_lock allocates a mutex that can be used for locking by the user. Use gensio_os_funcs_lock and gensio_os_funcs_unlock to lock/unlock the mutex. The gensio_os_funcs_free_lock will make sure the lock is not locked and free the data associated with the lock. Note that even for os funcs that don't implement locks, this will implement a counter to make sure that all locks are balanced.

gensio_os_funcs_get_monotonic_time returns a time value from the monotonic clock used for gensio_os_start_timer_abs. It can also be used as a standard monotonic clock, but is not a wall clock of any kind.

gensio_os_funcs_set_vlog must be called by the user to set a log handling function for the os funcs. If something goes wrong internally in the gensio library, this log function will be called to report the issue.

Timers are allocated with gensio_os_funcs_alloc_timer. When the timer expires, the handlers will be called with the given cb_data. This will return NULL if the timer cannot be allocated. Timers are not running when allocated, the must be started with gensio_os_funcs_start_timer, or gensio_os_funcs_start_timer_abs. The first starts a timer relative to the current time. The second starts a timer based upon a monotonic clock, see gensio_os_funcs_get_monotonic_time for details. These will return GE_INUSE if the timer was already running. To stop a timer, call either gensio_os_funcs_stop_timer or gensio_os_funcs_stop_timer_with_done. These both return GE_TIMEDOUT if the timer is not running. The first has no way to assure that the timer is finished running its handlers; the timer handler may still be active when it returns. If it does not return an error, the second will call the done_handler function when the timer is completely stopped and all the handlers are finished. The second also returns GE_INUSE if the timer has already been stopped but the done handler hasn't been called yet. Note that you cannot start the timer again until the done handler is called. And finally, to free a timer, use gensio_os_funcs_free_timer. The timer should not be running when calling this.

Runners are sort of like zero-time timers, they will just be called immediately. They are useful for escaping from deep locking situations where you want to do something at the base context. Use gensio_os_funcs_alloc_runner to allocate one of these. It returns NULL if the runner cannot be allocated. gensio_os_funcs_run causes the handler to be called. It returns GE_INUSE if the runner is currently already scheduled to be run. And gensio_os_funcs_free_runner frees the runner. It should not be currently scheduled to run.

gensio_os_funcs_service provides the main service function to run timers, runners, I/O handling, etc. If it is interrupted by a signal (on Unix), it returns GE_INTERRUPTED. If timeout is non-NULL, it is a relative amount of time to wait and this will return GE_TIMEDOUT if the timeout expires. Note that for long timeouts (days) this may return early on some os funcs, so don't rely on this for timing. When this returns, the timeout will be updated to any remaining time, even on an early timeout. Generally you don't use this function, you use waiters instead.

Call gensio_os_funcs_handle_fork in the child function after a fork (Unix only). This cleans up various things and readies the gensio library to be used after a fork. If this returns an error, it is likely that the gensio library is unusable on the child.

Waiters are used to wait for things to happen. When the thing happens occurs, that code should call wake to wake the waiter. Normal servicing of tiers, runners, I/O, etc. are done while waiting. Waiters and wakes are count based, if you call the wake before the wait that's ok, the wake will be waiting when the wait happens. If you call wake 3 times, there are 3 wakes pending. To allocate a waiter, call gensio_os_funcs_alloc_waiter. It returns NULL if it cannot allocate the waiter. gensio_os_funcs_wait waits for count wakeups to be done and then returns zero. If timeout is NULL it waits forever. Otherwise if the timeout expires it returns GE_TIMEDOUT and the timeout is updated to the remaining time. If this times out, no wakeups are "used" by the function, if only some are pending those are still pending. gensio_os_funcs_wait_intr is like gensio_os_funcs_wait except if an signal is received (Unix only) it will return GE_INTERRUPTED. gensio_os_funcs_wait_intr_sigmask is like gensio_os_funcs_wait_intr but allows a user-specified signal mask to be installed (Unix only). See gensio_os_proc_setup for details. To send a single wakeup to a waiter, use gensio_os_funcs_wake. And, of course, call gensio_os_funcs_free_waiter to free a waiter.

An os funcs has a single void pointer that the user may install some data in for their own use. Use gensio_os_funcs_set_data to set this data and gensio_os_funcs_get_data to retrieve it.


Return values are documented in the text above.


gensio_set_log_mask(3), gensio_get_log_mask(3), gensio_log_level_to_str(3), gensio(5), gensio_err(3)

23 Feb 2019