The Main Loop

2019

Asynchronous programming with libraries

Recap: Dispatcher

class Dispatcher:
    def __init__(self):
        self.handlers = {}

    def dispatch(self):
        with select.poll() as demultiplexer:
            for descriptor, handler in handlers.items():
                if handler.should_read():
                    demultiplexer.register(descriptor, select.EPOLLIN)
    
                if handler.should_write():
                    demultiplexer.register(descriptor, select.EPOLLOUT)

            for descriptor, event in demultiplexer.select()
                if event == select.EPOLLIN:
                    self.handlers[descriptor].handle_read()
    
                if event == select.EPOLLOUT:
                    self.handlers[descriptor].handle_write()

Recap: Main Loop

class MainLoop:
    def __init__(self):
        self.dispatcher = Dispatcher()
        self.running = False

    def run(self):
        self.running = True

        while self.running:
            self.dispatcher.dispatch()

    def quit(self):
        self.running = False

Main loop libraries

  • Xlib *

  • GLib

  • Qt

  • Twisted **

  • Tornado **

  • UV

  • Nodejs **

  • ReactPHP

Text

Text

* not really generic

** more than event loop

[censored]

Batteries included

Text

Text

Text

/* GLib */
g_child_watch_add(...)
g_dbus_get(...)
g_resolver_lookup_by_name_async(...)

// Qt
QProcess
QDbusConnection
QHostInfo

# twisted
esmtp_client = protocol.ClientFactory(protocol=smtp.ESMTPClient)
internet.TCPClient(esmtp_client)


// nodejs
client.query('SELECT $1::text AS message',
             ['Hello world!'],
             (err, res) => {
                 console.log(err ? err.stack : res.rows[0].message)
             })

"Call later"

Text

Text

Text

gboolean idle_task(gpointer user_data) {
    /* ... */

    return G_SOURCE_REMOVE;
}

int main() {
    GMainLoop *loop = g_main_loop_new(NULL, false);
    g_idle_add(idle_task, NULL);
    g_main_loop_run(loop);
}
void idle_task(void) {
    // ...
};

int main() {
    QEventLoop loop;
    QTimer::singleShot(0, idle_task);
    loop.exec();
}

Is this a TETimer?

Recursion

Text

Text

Text

bool DeathStar::really()
{
    QMessageBox dialog(QMessageBox::Question,
                       "Destroy Alderaan?",
                       (QMessageBox::Apply |
                        QMessageBox::Ok |
                        QMessageBox::Retry))

    int result = dialog.exec();
    return result != QMessageBox::No;
}

block?

loop = GLib.MainLoop()

def wait():
    GLib.timeout_add(1000, loop.quit)
    loop.run()

Glib.idle_add(wait)
loop.run()




block?

Thread pool

Text

Text

Text

static uv_loop_t loop;
static uv_work_t work;

void work_callback(uv_work_t *work) {
    /* runs in a thread */
    work->data = compute_result();
}

void work_complete(uv_work_t *work, int status) {
    /* runs in main loop */
}

uv_loop_init(&loop);
uv_queue_work(&loop, &work, work_callback, work_complete);

Custom handlers

Text

Text

Text


gboolean callback(GIOChannel *channel,
                  GIOCondition condition,
                  gpointer user_data)
{
    uint8_t buffer[1024];
    gsize bytes_read;
    GError error;

    GIOStatus status = g_io_channel_read_chars(channel,
                                               buffer, sizeof(buffer), 
                                               &bytes_read,
                                               &error);
}

GIOChannel *channel = g_io_channel_unix_new(fd);

int fd = ...;

g_io_add_watch(channel, G_IO_IN, callback, NULL);

Third party handlers

Text

Text

Text

/* libpq (postgresql) */
int PQsocket(const PGconn *conn);
int PQconsumeInput(PGconn *conn); /* readable */
int PQflush(PGconn *conn);        /* writable, repeat until returns 0 */
/* libmosquitto (MQTT) */
int mosquitto_socket(struct mosquitto *mosq);
bool mosquitto_want_write(struct mosquitto *mosq);
int mosquitto_loop_read(struct mosquitto *mosq, int count);  /* readable */
int mosquitto_loop_write(struct mosquitto *mosq, int count); /* writable */
int mosquitto_loop_misc(struct mosquitto *mosq);             /* periodic */
/* libdbus */
int dbus_watch_get_unix_fd(DBusWatch *watch);
unsigned int dbus_watch_get_flags(DBusWatch *watch);
dbus_bool_t dbus_watch_handle(DBusWatch *watch,	unsigned int flags);

Teaser trailer: promises

Text

Text

Text

var promise = new Promise(function (resolve, reject) {
    var c = new Connection();

    c.on_connected(resolve);
    c.on_timeout(reject);

    c.connect('silvair.com', 443);
});

function great_success(result) { };
function utter_failure(error) { };
  
promise.then(great_success).catch(utter_failure)

Thank you

Questions?

Main Loop vol 2, libraries @ Silvair

By Michał Lowas-Rzechonek

Main Loop vol 2, libraries @ Silvair

Asynchronous programming in an embedded environment

  • 624