Elektrified X.org released (was: X configuration paradigm, and a proposal)

Sean Middleditch elanthis at awesomeplay.com
Wed Dec 8 05:22:56 EET 2004


On Tue, 2004-12-07 at 23:32 -0300, Avi Alkalay wrote:
> On Sun, 5 Dec 2004 20:00:59 -0500, Avery Pennarun <apenwarr at nit.ca> wrote:
> > My advice continues to be that you should support simple callback
> > registrations: call this function when this keyset changes.  Don't call this
> > function anymore when this keyset changes.  Poll for changes now and call
> > any registered events.  (And be aware that the "poll" function might not
> > *actually* be necessary in some implementations.)
> 
> Any suggestion on how to implement this in a libc-like library ?
> Callbacks imply in having a paralel execution, outside the main
> program, waiting for an event, or polling. A.K.A threads, forks,
> daemons, or event loops. Am I right ?
> Its not clear for me if signals and alarms can help here....

Callbacks do not require that you drop polling.  In fact, you are still
going to *require* polling.  Inotify requires polling.  It's just a
different kind of polling than stat()ing a bunch of files in a loop.

The API would still have a "read updated keys" function.  With a
callback API, however, before calling this function, you'd provide a
list of keys you are interested in and a list of callback functions
using callback registration functions.  The programmer calls the
"read_updates" function and know that, when it returns, all callbacks
for all modified keys have been called and are finished executing.

For your existing backend, this has absolutely no change in terms of
performance.  However, for when change *notification* is available, then
the callback API would work a little differently.  When you registered
the callbacks, the monitoring/notification daemon would get the list of
keys your app is interested in.  It wouldn't know anything about the
callbacks themselves.  All it has is a list of keys and a file
descriptor open to communicate with the app (UNIX domain socket, most
likely).

That descriptor can then be used in an application's main loop.  Almost
every complex interactive application is written the same general way:

main_loop {
  wait_for_event
  process_events
}

The problem with your current API is that you can't code that loop.  Say
I have a server app - the events I want to listen to are: incoming
connection, data from connected client, and configuration change.  I
need to be able to do all of that with a single "wait_for_event" call.
Then, when an event comes in, I have to be able to process it.

The callback API would work like so.  My wait_for_event would basically
be a select()/poll()/epoll() syscall with a bunch of file descriptors -
user input, sockets, whatever - plus the descriptor open to the
configuration notification daemon.  When that descriptor has data to
read, I call the kdb_update_keys() function (or whatever you decide to
call).  This function just reads in a change notification structure from
the socket (of course being careful to handle sockets right, as so very
few developers seem to do, from my experience pouring over *lots* of
network code... but that's an unrelated topic).  These structures are
basically just a key name and a value.  The function looks through the
list of callbacks you gave the library, matches up the key name, and
then invokes the callback with the change structure data.

Instead of using a custom protocol, you could also just use D-BUS, which
very well could be a better idea.  If you assume that, once the base
system is up, the notification daemon is up (and, as consequence for
both consistent notification, atomic transactions, and general data
integrity, that *all* access to the key database goes through said
daemon) then you wouldn't even really need to enforce any client-library
on apps - they could just directly use D-BUS.

A quick C-like pseudo example follows.  With this system, any time a
monitored key changes, the app gets notified using its poll() call the
same way an app gets notified about a D-BUS message, X11 event,
whatever.  It then reads in the key change notification and invokes the
proper callbacks.

main
{
  int option_flag;

  option_flag = kdb_get_key_int("/myapp/mykey1");

  kdb_register_callback("/myapp/mykey1",
    (kdb_callback_t)my_key1_changed, &option_flag);
  kdb_register_callback("/myapp/mykey2", 
    (kdb_callback_t)&my_key2_changed, NULL);

  pollfds[0].fd = server_socket;
  pollfds[1].fd = kdb_get_poll_fd();

  do {
    ret = poll(pollfds, 2, flags);
    handle_error(ret);

    if (pollfds[0].revents & READ)
      handle_client(pollds[0].fd);
    if (pollfds[1].revents & READ)
      kdb_handle_changes(); /* callbacks get called from here */

    if (option_flag)
      do_optional_behavior();
  } while(running);

  return 0;
}

void
my_key1_changed (char* key_name, char* value, void* cookie)
{
  *(int*)cookie = atoi(value);
}




More information about the xdg mailing list