[2951] in BarnOwl Developers
[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