[2951] in BarnOwl Developers

home help back first fref pref prev next nref lref last post

[RFC] GMainLoop branch

daemon@ATHENA.MIT.EDU (David A Benjamin)
Thu Mar 3 12:21:06 2011

Date: Thu, 3 Mar 2011 12:21:04 -0500 (EST)
From: David A Benjamin <davidben@MIT.EDU>
To: barnowl-dev@mit.edu

Continuing on the pattern of MORE GLIB THAN YOUR BARNOWL HAS ROOM FOR, 
here is a branch that backs owl_select onto glib's main loop. Unlike the 
other files of glib, this one's diffstat is not very good-looking. On the 
contrary, I'm adding near 200 lines of code.

What we do gain from this main loop is (other than more Gs[1]) 
thread-safety. Because what BarnOwl really needs is threads, right? Glib's 
main loop allows for threads to post messages to each other, which will be 
useful if we want to, say, put file access on a separate thread so we're 
still responsive when AFS blows up. In fact, Adam's been working on a 
patch on top of this branch to move logging to a worker thread which I 
think people have been asking for. This may also come in handy for a 
future sqlite-backed BarnOwl so we needn't block on the database.

The documentation of glib's event loop here is:

http://library.gnome.org/devel/glib/stable/glib-The-Main-Event-Loop.html

As an overview (and because it's not actually well documented; I had to 
source-dive a bit), the main structure is a GMainContext which belongs to 
a particular thread and contains a number of GSources. To run a loop you 
make a GMainLoop attached to the GMainContext and run it. (I imagine 
they're separated so you can run nested event loops. Nevermind that this 
is madness and the source of no end of re-entrancy bugs in graphical 
programs.) GMainContexts are thread-safe and may be posted to from other 
threads.

A GSource has three hooks (and a destructor): prepare, check, and dispatch 
and a number of fds attached. Dispatch is fairly straight-forward. Prepare 
and check return whether the source is ready to run. Prepare runs before 
select and sets a minimum timeout. Check runs afterwards and has access to 
the results of a select (actually a poll(2)) on its file descriptors. If 
either returns true, the main loop may call dispatch. They're analogous to 
ZQLength() and ZPending(). fds may be changed at any point (in particular 
during prepare), making the AIM hack less of a hack. Still ugly though.

GSources also has this hard priority system. At any loop iteration, only 
the highest priority GSources are ever run. So, you aren't guaranteed that 
your dispatch will run if prepare/check return true or that prepare/check 
will run at all. The current version of this branch doesn't use priorities 
at all, but we could do so. (E.g., always prefer keypresses and redraws to 
a pile of incoming zephyrs. Or separate resize/redraw and process resizes 
first; I think GTK does this.)

So, in theory this branch is fairly straight-forward; add GSources that 
bridge our events and glib's. There's some muckiness with pre-select 
actions (I converted them to GSources directly; if we want to use the hard 
priority system, we want to split the check portions into the appropriate 
callbacks or lower priority sources will never fire.)

There is one complication: UNIX signals are apparently totally idiotic. 
AFAICT, signal masks are per-thread, but signal dispositions are 
per-process. When you send a signal to process as opposed to a particular 
thread, you look at all the threads with this signal masked and... pick 
one arbitrarily. (What the hell!???) And the same handler runs no matter 
what thread gets picked. So, really the only sane way to deal with this is 
to have a dedicated signal thread that loops on sigwait and mask those 
signals on all threads. On the plus side, our signal handlers are now much 
much simpler because we don't have to worry about getting one during a 
malloc.

On the other hand, if perl code decides to say, unmask a signal we care 
about or set a signal handler, badness may ensue. The former isn't so bad; 
threads in sigwait get priority over others, so we'll have a race 
condition. If it sets a signal handler, I don't know how perl reacts when 
its signal handler finds itself running on the wrong thread. Probably 
badly. We could mask every signal ever on the other threads, but I'm 
tempted to just say "don't do that".

So... thoughts? Do we want to go down this path?

David

[1] In fact,
% echo $(expr $(git diff origin/master | grep '^\+' | grep -cio 'g') \
             - $(git diff origin/master | grep '^\-' | grep -cio 'g'))
106

The following changes since commit 9620c8d92ecfed30b4b6c4d5d6861179e2ab418d:

   Load Encode module for IRC. (2011-03-02 13:10:29 -0500)

are available in the git repository at:
   git://github.com/davidben/barnowl.git g_main_loop

David Benjamin (21):
       Start of GMainContext code
       Make the owl_window redraw hook a GSource
       Also make owl_process_messages a GSource
       Add a zephyr GSource
       First go at sigwait-based signal handling
       Punt pre-select actions
       Make owl_signal_init take a signal-thread callback
       Allow interrupts to be taken at any point
       Add a GSource for AIM events
       Remove owl_select
       Remove owl_select_aim_hack and _owl_select_timer_eq
       Just ignore SIGPIPE altogether
       Don't deliver SIGALRM through the signal thread
       Reset signal dispositions and mask, in case our parent was lame
       Move owl_process_input_char to owl.c
       Remove OwlGlobalNotifier typedef
       Cleanly destroy the signal thread on shutdown
       Add owl_select_post_task helper function
       Use sigwaitinfo instead of sigwait
       resizepending needn't be sig_atomic_t anymore
       Push stderr redirection onto a worker thread

  Makefile.am  |    3 +-
  aim.c        |  115 ++++++++++++++-
  filterproc.c |    4 -
  functions.c  |   29 +----
  global.c     |   83 +++++------
  owl.c        |  225 +++++++++++++----------------
  owl.h        |   19 +--
  select.c     |  454 +++++++++++++++++++++++-----------------------------------
  signal.c     |   62 ++++++++
  stderr.c     |   75 ++++++++++
  window.c     |   50 +++++++
  window.h     |    2 +
  zephyr.c     |   73 ++++++++--
  13 files changed, 688 insertions(+), 506 deletions(-)
  create mode 100644 signal.c
  create mode 100644 stderr.c

home help back first fref pref prev next nref lref last post