Re: Moving main loop to GLib.




Kenneth Albanowski <kjahds@kjahds.com> writes:

> On Wed, 18 Nov 1998, Owen Taylor wrote:
> 
> > Moving the main loop to GLIB is desirable, because it enables
> > non-GUI applications to take advantage of a good event
> > loop and of event driven programming. In particular, it
> > would be desirable to use the main loop for ORBit.
> > 
> > The main reason why this operation is not simply copying
> > code is that the X events from GDK need to be integrated
> > into the event loop. To facilitate this, the proposed
> > GLIB interface is meant to be extensible. The core
> > code polls and dispatches events from various "event sources".
> 
> Agreed, all around. I've been writing some server code recently, among
> other stuff, and I would very much appreciate a generalized and portable
> event-loop API.
> 
> However, I've also played around at integrating dissimilar event-handling
> code (GNU readline & Tk, for example), and realize the difficulties
> involved. 
> 
> > [...API...]
> 
> This looks sufficient for basic functionality, but programmers might want
> to request specific poll() events. Also, the notion of prioritized events
> might be needed. 

Prioritized events where in that proposal... for instance, I
have:

 guint     g_idle_add_full       (gint                 priority,
                                  GFunc                function,
                                  gpointer             data,
                                  GDestroyNotify       destroy);

and the same for timeouts and input functions.
 
> > [...GSource API...]
> 
> But be careful not to override the local poll() mechanism (if it exists),
> as poll() is designed to be extensible, and the programmer may want to use
> the extended capabilities, without delving to the GSource level.

Hmmm, I suppose one could replace the input_add() functionality
with a poll_add():

 gint      g_poll_add      (gint                 source,
                            GPollEvents          events,
                            GPollFunction        function
                            gpointer             data);

I'm a little nervous about this, since it only really is
an advantage if one says that one can use the platform
specific constants for 'events'. One could, alternatively,
just add some more of the common poll events into
GInputCondition. (Though one can't assume that they will
be honored, since the entire poll, might, in any case,
be emulated via select().
 
> > The algorithm is, for each run of g_main_iteration(), we:
> > 
> > 1) While the list of currently pending sources is non-empty
> >    call (*restart) on the head, and (*dispatch) on the rest,
> >    removing sources from the list after each returns
> > 
> > 2) Call (*prepare) for each source.
> > 
> > 3) Poll with the union of the pollfd's from each source,
> >    and the minimum of all the timeouts from all sources.
> >    (timeout < 0 == infinite)
> 
> OK, I see what you are doing here. The problem is, this assumes that
> poll() (or emulation) can be used to poll all GSource's simultaneously,
> which is definitely not always the case under other operating systems (and
> I'm sure cases for Unix could be designed, looking at directory contents,
> for example.) 

Unix certainly has events that don't fit well into the poll() / select()
model. The two most interesting examples are SIGCHILD and signalling
between threads. Unfortunately, I don't know of _any_ good way
of handling these together with FDs, except for using fork()
or extra threads and communicating with the main thread via
pipes.

> Indeed, the idea that a GSource always has a set of
> poll_fd's (which contain file descriptors in turn) isn't appropriate.

The point of the central poll() is not that every sort of
interesting event occurs via a file descriptor, but, rather
that you can only wait in one place. I'd go so far as to
say that in fact, the whole point of an 'event loop' is not
the events but the waiting.

You can add as many polling (in the sense of spin-waiting)
event sources - they merely specify timeouts of 0. But, as
far as I can see, it doesn't make sense to add another 
type of event source that has a different mechanism for waiting.

> Tcl/Tk goes some way towards this end (necessary, considing its support
> for Win'95), and makes a useful model, to some degree.
> 
> To try and summarize what is needed: we need something that I'll call
> "Providers" (which I confused your Sources for) which are like poll() and
> select(): given a set of opaque "tags" of a certain type, they will return
> the first (and/or all, depending on various conditions) tags that match. 
> poll() and select() operate exactly in this manner, with
> "tags" of the file-descriptor and timeout type.
> 
> The problem, of course, is how to check with multiple Providers without
> going into unneccesary spinloops. Unfortunately, I'm not aware of any
> perfect solution for this, but consider some of the constraints we may
> have: Providers that don't support timeouts at all, either waiting an
> infinite period, or only supporting a "peek" with immediate timeout. Or
> only timeouts of a limited nature might be available (integral seconds).
> 
> Even more confusing is interacting providers: one provider might only be
> able to say whether the event is ready now or not (just a peek), but the
> OS is capable of interrupting _other_ provider timeouts when the event
> becomes ready. 

If the OS can do this, then the single-point-of-waiting model 
works fine.
 
> As I said, Tcl/Tk goes some way towards supporting this, but I'm not sure
> how general-purpose the code is. (It definitely has the concepts of
> Providers, Sources, etc., but I'm not sure whether the logic is up to
> dealing with arbitrary types of Providers with arbitrary limits.)

It looks like the Tcl/Tk code is pretty similar to my proposal,
all things considered; there is preparation, a central "wait stage",
and then a check stage afterwords. The main difference is
that the 'GSource' structures are platform independent, and
the on Unix sources use a separate mechanism to register
interest in particular fd's. 

Thanks for the reference.

[ This similarity is probably not completely coincidental.
  I based my proposal on Graham Barr's Event:: module
  for Perl, and, I think, came out of discussions related
  to the Perl/Tk ]
 
> In case this all seems a bit moot, I should point out that Windows has an
> extraordinary number of different event sources going on at one time, and
> that it wouldn't be at all difficult to need to monitor several different
> sources. (Windows '95 is worse then NT, at 95's sockets are not usable as
> file handles, and thus cannot be be used by the equivalent of select().) 
> 
> COM ports can be handled differently from files (and need to be, in some
> cases), which can be handled differently from sockets (and need to be,
> under '95), which (as I recall) are handled differently from directory
> events, which are all handled _completely_ differently from process,
> mutex, and other synchronization objects which are in another layer
> altogether.
> 
> (You thought UNIX was bad? Windows is much much worse. Yes, it can handle
> some event sources, like directory changes and task-exit notification,
> that UNIX cannot, but it has all been supported by cramming in an
> incredible variety of event and "descriptor" types.)

[ Really off-topic: select/poll-on-directory should work fine for
  fitting directory changes into the Unix model. I believe it even 
  has been  blessed as a "good idea" for going into future
  Linux kernels ]

> Personally, I would much prefer for GLib to have main-loop functionality
> that is at least equal (preferably superior) to that of Tcl/Tk (and
> everything else we can find). At the very least, each GSource should be
> inserted into a GProvider (or whatever you want to call it), with one
> GProvider being standard under UNIX. For now, we can probably skip
> worrying about user-installed GProviders, and rely on the GLib porter to
> figure out what specific GProvider's are necessary for an OS, and just how
> to mediate between them. 

I'm not sure that my GSource's _are_ indeed different things
than your GProviders.

As I see it, the structure of the main loop is:

- There are various event types (idles, input functions, etc.) -
  Modulo things like file descriptors, these event types
  can be reasonably cross-platform.

- The event types map into specific platform-specific mechansims
  for waiting and notification

- There is a generic interface (g_main_run() and friends) for
  controlling the process of waiting and notification.

Because the mapping from an event type to a wait-mechanism
is by its very nature platform-specific, I don't think
it makes sense to have a platform-indepedent interface for the 
middle layer, which, it seems, is what your GProvider is.

Instead, I think the middle layer probably has to be
platform specific. GSource (perhaps it should be
GUnixSource), is a generic, but Unix-centric middle layer.

It might, however, be possible to structure things so the 
GSource's are platform independent, as in Tk, though that 
mostly is a question of shoving the platform-dependence 
somewhere else in the API.

Regards,
                                        Owen



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]