Async linux demo

../_images/example_async_linux.png

This example showcases a number of different things:

  • Emitting signals

  • Using a state condition guard

  • De-coupling the state machine with a thread safe queue

  • Timer events using linux’s timerfd

  • Event queue using linux’s signalfd

  • epoll -based main event loop

This example uses a ‘private context’ for the machine:

struct private_ctx {
    struct ufsm_queue *q;
    struct ufsm_timer tmr, tmr_exit;
    int epoll_fd;
    bool run;
};

This is passed to all action functions through the ‘context’ parameter.

Main loop:

/* Initialize the queue to hold 128 int's */
q = ufsm_queue_init(priv.epoll_fd, 128);
priv.q = q;

ufsm_timer_init(&priv.tmr, priv.epoll_fd, q, 1500, eEnable);
ufsm_timer_init(&priv.tmr_exit, priv.epoll_fd, q, 5000, eExitTimerExpired);

linux_async_init(&m, &priv);
linux_async_process(&m, UFSM_RESET);

for (;;) {
    int no_of_fds = epoll_wait(priv.epoll_fd, events, MAX_EVENTS, -1);

    if (no_of_fds == -1) {
        fprintf(stderr, "epoll_wait failed\n");
        rc = -1;
        goto err_free_timers_out;
    }

    for (int n = 0; n < no_of_fds; n++) {
        int fd = events[n].data.fd;

        if (ufsm_timer_get_fd(&priv.tmr) == fd) {
            ufsm_timer_handle(&priv.tmr);
        } else if (ufsm_timer_get_fd(&priv.tmr_exit) == fd) {
            ufsm_timer_handle(&priv.tmr_exit);
        } else if (ufsm_queue_get_fd(q) == fd) {
            uint64_t no_of_queue_events;

            if (ufsm_queue_handle(q, &no_of_queue_events) != 0) {
                fprintf(stderr, "Failed to read queue events\n");
                priv.run = false;
                rc = -1;
                break;
            }

            for (int i = 0; i < no_of_queue_events; i++) {
                assert(linux_async_process(&m, ufsm_queue_pop(q)) == 0);
            }
        }
    }

    if (!priv.run) {
        printf("Exiting...\n");
        break;
    }
}

The queue, timers and the state machine are initialized. The ‘tmr’ timer is used to send the event ‘eEnable’ after 1500 ms.

The ‘tmr_exit’ timer will send ‘eExitTimerExpired’ after 5000ms and terminating the program.

The queue uses a signalfd ‘object’ to wake up the epoll when there are new events in the queue.

Source code: examples/linux