[plt-scheme] Make multiple attempts to open DrScheme join running process?

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Mar 5 20:46:45 EST 2007

At Sun, 25 Feb 2007 08:41:48 -0600, Matthew Flatt wrote:
> Offhand, I don't know how double-clicking works these days in X, so
> I'll have to learn about when and how an application is supposed to
> find an existing instance of itself. I expect I can improve things
> after I become better informed, but only for future versions.

I didn't find a clear answer, and different applications and libraries
seem to work in different ways. Some implementations have race
conditions that I want to avoid. Some implementation rely on extra
servers that are not always available with X11. Some traditional Unix
approaches (based on PID-identifying files in the user's directory)
require extra effort to mesh with the X model, where multiple machines
can connect to a single X display, and where a single machine can host
multiple X displays.

Below I describe the new DrScheme/MrEd interface for single-instance
mode, as well as its internal implementation. I worry about having made
up an interface and implementation; advice from X11 experts would be
welcome.

The current implementation is available via SVN.

Matthew


External Interface
------------------

A DrScheme user enables single-instance mode by supplying
"-singleInstance" as a command-line argument. So, map a double-click of
a ".scm" file to "drscheme -singleInstance" instead of just "drscheme".


At the MrEd level, the external interface requires more explanation. By
analogy to Windows and Mac OS X, support for single-instance mode is
built in to the MrEd layer and inherited by all MrEd applications. That
is, since MrEd programmers get single-instance support under Windows
and Mac OS X for free, they should also get it for free under X11. As
under Mac OS X, though, X11 end users should have some control over
whether single-instance mode is used; for historical and cultural
reasons, single-instance mode is not be the default.

MrEd's new "-singleInstance" flag is treated like a standard X flag
(like "-display"), which means that it is passed on to MrEd when
supplied to launcher scripts like "drscheme".

When "-singleInstance" is supplied, before loading anything else
specified on the command line, MrEd looks for an existing instance of
itself with

 * the same X display;

 * the same application name (possibly specified via the -N flag, which
   is supplied by launcher scripts, so that "drscheme" is a different
   application than "mred" or "help desk") turned into an absolute
   path;

 * the same hostname, so that connections to the same X display from
   different hosts produce separate instances; and

 * the same version number (which should be redundant given the other
   tests, but seems like a good to include just in case).

If an existing instance is found, then all command-line arguments
(after MrEd options) are treated as pathnames, and are sent in absolute
form to the discovered instance via the application file handler (as
set by `application-file-handler'). The current instance then exits.
Otherwise, the start-up process continues as usual.


Internal Implementation
-----------------------

The basic implementation strategy is to have instances find each other
and communicate through the X server. This strategy prevents confusion
between instances running in different X displays, and it obtains
synchronization in a CML-like way (i.e., all sharing goes through a
single process, which is the X server).

The protocol described below avoids race conditions and deadlock in
electing the active instance --- I think. The protocol probably even
has a name (someone might clue me in). If the overall protocol is
right, there's still a question of whether I'm using X primitives
correctly to implement the protocol, so I try to provide all relevant
details.


When MrEd starts, it creates a hidden X window to manage clipboard
operations. This window is now given an initial X property whose name
is based on the application, hostname, and version; the property
identifies the window as representing a CANDIDATE instance of the
application.

After setting the property, MrEd inspects all windows in the current
display to see if any window has either

 1. a different X property based on the application+hostname+version
    that identifies the window as representing the ACTIVE instance of
    the application.

      In this case, a sequence of ClientMessage events are sent to the
      window to transfer the current instance's arguments (as
      filenames) to the active instance, and the current instance
      exits.

 2. the CANDIDATE property.

      In this case, the current MrEd's hidden window keeps its
      CANDIDATE property only if the other window has a higher ID than
      the current instance's window. The current instance then starts
      again checking all windows in the display. (Eventually, there
      will be only one candidate, which will then designate itself as
      the active instance.)

If no other window is found with either property, and if the current
instance is still a candidate (i.e., the instance's window still has
the CANDIDATE property), the the current instance becomes the active
instance. It sets the ACTIVE instance property on its hidden window.
(The CANDIDATE property is not removed, though it could be removed
after the ACTIVE property is added.) The usual start-up process
continues in the active instance.

It's possible to find no windows with either property, including the
current instance's own window. That happens when some candidate
instance with a lower window ID exits before the current instance sees
it as the active instance. In that case, the current instance restores
its window's CANDIDATE property and starts looking again.


The shared X server provides a kind of sequential consistency: if two
instances become candidates at roughly the same time, then at least one
of them sees the other. Also, shared window IDs provide an ordering
that helps the different instances choose consistently among
themselves. Livelock seems possible only if new candidates keep showing
up fast enough (i.e., not possible in practice given MrEd's start-up
time).

By transferring command-line arguments through ClientMessage events, an
instance doesn't have to negotiate further with the active instance
before exiting; it's enough to get all the messages to the X server
(i.e,. to XFlush() before exiting). An active instance, meanwhile, has
to accumulate information from individual ClientMessage events ---
possibly interleaved with events from other instances --- until it
receives an event that indicates an end-of-sequence; but, that's easy.
Since command-line arguments are relatively small, sending roughly 16
bytes at a time through events works fine (as opposed to using the X
selection mechanism, which supports larger data streams but requires
much more negotiation).



Posted on the users mailing list.