Galaxy Communicator Documentation:

Upgrading from 2.0 to 2.1

License / Documentation home / Help and feedback

In almost all cases, upgrading from 2.0 to 2.1 will be completely transparent.


Step 1: Configuring your system

The 2.1 distribution contains two major changes in your config.make file.

First, because the Galaxy Communicator distribution no longer contains any MIT servers, there are no longer any settings for contrib/MIT. Second, it is now possible to compile Galaxy Communicator libraries as shared libraries, which reduces the size of executables, but introduces a run-time dependency on the shared library. MITRE's experience suggests that using shared libraries can reduce the size of your Galaxy Communicator installation by 50%. See the installation notes for details.

It's possible that you've included explicit dependencies on static libraries (.a) files in your Makefiles. Any examples you have which use the MITRE utilities library may have such references. If you intend to preserve the option of compiling with shared libraries, you will need to change those references. For instance, here's a fragment of a Makefile based on the Communicator Makefile template, in version 2.0 and version 2.1:

Version 2.0

LIBDEPS += $(MITRE_ROOTDIR)/utilities/lib/$(ARCHOS)/libMITRE_galaxy.a

Version 2.1

LIBDEPS += $(MITRE_ROOTDIR)/utilities/lib/$(ARCHOS)/libMITRE_galaxy$(LIBEXT)


Although we haven't tried it, config.make files for 2.0 should still work in 2.1; you simply won't have the option of shared libraries.


Step 2: Check your use of none! and destroy!

In version 2.0, the program file special directives none! and destroy! had the following interpretations: In version 2.1, the behavior of these directives has been redefined in terms of the CONTROL: directive. As far as we can tell, none! still behaves identically, but destroy! now behaves identically to none!, except that the program stops executing immediately after the rule. Because destroy! no longer waits until the message return to stop execution, certain rule sequences will no longer work as before. For instance, destroy! followed by none! will fire both rules in 2.0, but only the first in 2.1:
RULE: ...
...
OUT: destroy!

RULE: ...
...
OUT: none!

This is a very subtle change, so inspect your program files carefully.


Step 3: Check your use of SET: and DEL: with dispatch_to_main

As a special case in versions previous to 2.1, the SET: and DEL: directives applied to the message, not the token, when the dispatch function was builtin.dispatch_to_main. This special case has been eliminated in version 2.1. The following program file illustrates the contrast:
SERVER: builtin
OPERATIONS: dispatch_to_main

INITIAL_TOKEN: {c foo :baz 6 }

PROGRAM: foo

RULE: --> dispatch_to_main
IN: :baz
SET: :bar 5
DEL: :baz

PROGRAM: main

Previous to version 2.1, :baz was deleted from the message and :bar was added, with the effect that :bar and not :baz was transmitted to the main program. In 2.1, neither change applies to the message, so :baz and not :bar is transmitted to the main program. This is a very subtle change, so inspect your program files carefully. The clean and proper way to transmit values from the foo program to the main program is and has always been to include them in the IN: directive:
RULE: --> dispatch_to_main
IN: :baz (:bar 5)

Step 4: Upgrade your own main loop (advanced, optional)

In order to support the new listener-in-Hub functionality, the function GalSS_InitializeServerToplevel() is now preferred to GalSS_InitializeServer(). In addition, it is now much more straightforward to customize server settings both before and after you parse command line arguments.

Unless you're embedding a Communicator-compliant server in another top-level loop, you won't need to worry about this; but if you do, there are a number of differences in 2.1 that you should be aware of. Here is the main() from the MITRE example fd_double.c, with the potential differences in boldface:

Version 2.0

int main(int argc, char **argv)
{
  GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) NULL;

  gcr = (GalaxyCallbackRecord *) malloc(sizeof(GalaxyCallbackRecord));
  gcr->timer_cb = (TimerCallback *) NULL;
  gcr->server_sock = -1;
  gcr->l = SM_NewLooper();

  gcr->gcomm = GalSS_InitializeServer(-1, 10, 0, 1, GAL_LOOP_EXTERNAL, 0, argc, argv);

  if (!gcr->gcomm) {
    fprintf(stderr, "Couldn't create a server\n");
    fflush(stderr);
    exit(1);
  }
  gcr->server_sock = GalIO_GetServerListenSocket(gcr->gcomm);
  SM_AddFDCallback(gcr->l, gcr->server_sock, DoubleServerHandler,
                   (void *) gcr);
  SM_Mainloop(gcr->l);
  exit(0);
}

If you aren't interested in the new listener-in-Hub functionality, nor interested in automatic parsing of Communicator-specific command line arguments, this example will function in 2.1. If you are interested in making a minimal change to obtain access to the listener-in-Hub functionality, you can do this:

Version 2.0/2.1 (not recommended)

int main(int argc, char **argv)
{
  GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) NULL;

  gcr = (GalaxyCallbackRecord *) malloc(sizeof(GalaxyCallbackRecord));
  gcr->timer_cb = (TimerCallback *) NULL;
  gcr->server_sock = -1;
  gcr->l = SM_NewLooper();
#if GC_VERSION < 0x20100
  gcr->gcomm = GalSS_InitializeServer(-1, 10, 0, 1, GAL_LOOP_EXTERNAL, 0, argc, argv);
#else
  gcr->gcomm = GalSS_InitializeServerToplevel(-1, 10, 0, 1, GAL_LOOP_EXTERNAL,
                                              0, -1, GAL_LISTENING_SERVER,
                                              (char *) NULL, (char *) NULL, argc, argv);
#endif

  if (!gcr->gcomm) {
    fprintf(stderr, "Couldn't create a server\n");
    fflush(stderr);
    exit(1);
  }

  /* If the server is acting as a client, we'll want to
     set up callbacks for the connections; if it has its own
     listener, we'll want to set up a callback for the server,
     which will set up the listeners for the connections as
     they're received. */

  if (GalIO_ServerListenStatus(gcr->gcomm) == GAL_LISTENING_SERVER) {
    gcr->server_sock = GalIO_GetServerListenSocket(gcr->gcomm);
    SM_AddFDCallback(gcr->l, gcr->server_sock, DoubleServerHandler,
                     (void *) gcr);
  } else {
    /* Add a callback for the connections. */
    GalIO_OperateOnConnections(gcr->gcomm, (void *) gcr,
                               __AddConnectionCallback);
  }

  SM_Mainloop(gcr->l);
  exit(0);
}

You can now modify the call to GalSS_InitializeServerToplevel() to modify the listen status and declare the host and port you wish to contact. The second change is sensitive to the listen status, and will start up the server main loop appropriately. However, these changes aren't particularly useful unless you can pass in the host and port as command line arguments, and since the Communicator libraries already provide this functionality, we've made it easy in 2.1 to exploit it.

Version 2.1 (recommended)

int main(int argc, char **argv)
{
  GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) NULL;
  int new_argc;
  char **new_argv;
  GalSS_ServerArgs *arg_pkg;

  gcr = (GalaxyCallbackRecord *) malloc(sizeof(GalaxyCallbackRecord));
  gcr->timer_cb = (TimerCallback *) NULL;
  gcr->server_sock = -1;
  gcr->l = SM_NewLooper();

  /* If you want to use the built-in server arguments, you
     can use GalSS_ExtractServerArgs. Otherwise, you can just
     call GalSS_InitializeServerToplevel(). */

  arg_pkg = GalSS_DefaultServerArgs();

  /* Make sure it knows that we're using our own main loop. We set this
     before we ever parse the server arguments, because we don't even want
     the arguments pertaining to the loop type enabled for the user. */
  GalSS_SAFixLoopType(arg_pkg, GAL_LOOP_EXTERNAL);
  arg_pkg = GalSS_ExtractCmdlineServerArgs(arg_pkg, argc, argv,
                                           &new_argc, &new_argv);

  if (!arg_pkg) {
    /* Something bad happened, or -help was passed. */
    exit(1);
  }

  /* Now, we call GalSS_InitializeServerFromServerArgs, and we don't have
     to worry about the signature of GalSS_InitializeServerToplevel. */

  gcr->gcomm = GalSS_InitializeServerFromServerArgs(arg_pkg,
                                                    new_argc,
                                                    new_argv);
  GalSS_FreeArgPkg(arg_pkg);

  if (!gcr->gcomm) {
    fprintf(stderr, "Couldn't create a server\n");
    fflush(stderr);
    exit(1);
  }

  /* If the server is acting as a client, we'll want to
     set up callbacks for the connections; if it has its own
     listener, we'll want to set up a callback for the server,
     which will set up the listeners for the connections as
     they're received. */

  if ((GalIO_ServerListenStatus(gcr->gcomm) & GAL_SERVER_TYPE_MASK) ==
      GAL_LISTENING_SERVER) {
    gcr->server_sock = GalIO_GetServerListenSocket(gcr->gcomm);
    SM_AddFDCallback(gcr->l, gcr->server_sock, DoubleServerHandler,
                     (void *) gcr);
  } else {
    GalIO_ServerCheckHubContacts(gcr->gcomm);
    if (GalIO_GetServerNumConnections(gcr->gcomm) == 0) {
      fprintf(stderr, "Couldn't contact any Hubs\n");
      fflush(stderr);
      exit(1);
    }
    /* Add a callback for the connections. */
    GalIO_OperateOnConnections(gcr->gcomm, (void *) gcr,
                               __AddConnectionCallback);
  }
  SM_Mainloop(gcr->l);
  exit(0);
}

In this final version, the first difference is that 2.1 includes support for using the built-in command line argument parsing tools to "peel off" the standard server arguments, using the functions GalSS_DefaultServerArgs(), GalSS_InitializeServerFromServerArgs(), and GalSS_FreeArgPkg(). Before you extract the server args with GalSS_ExtractServerArgs(), you can make whatever changes to the argument package you choose; here, for instance, we're telling the server that it's not going to run its own main loop. Because of this functionality, you will not need to worry about the function signature for GalSS_InitializeServerToplevel(), nor do you have to recapitulate the Communicator argument parsing if you desire that level of flexibility. If you don't want to give the user access to some of these settings, of course, you can either overwrite them between the first two function calls, or simply use GalSS_InitializeServerToplevel() directly.

The second difference is that once we permit the listener-in-Hub functionality, we need to check the listen status of the server and initialize our own listeners appropriately. In this case, if the server has a listener in it, we add a file descriptor callback for the server listener; otherwise, if the server is contacting Hubs, we check the Hub contacts and then call GalIO_OperateOnConnections() with an operation which makes sure that reinitialize messages that are queued as part of the listener-in-Hub handshake are actually processed.

Notice that because the default C behavior for listener-in-Hub functionality is to retry both for initial connections and after disconnects, this loop doesn't conform to the default C behavior because it's only possible to implement that sort of retry using timers, and this example only uses file descriptor callbacks.


Step 5: Upgrade timed task invocation (advanced)

Version 2.0 had a logic problem involving the interaction of sockets and the timed task loop on Win32. To fix this bug, the function Gal_AddTimedTaskWithIO() had to be eliminated, and replaced with the new functions Gal_AddTimedTaskWithSocketIO() and Gal_AddTimedTaskWithFileIO(). The chances are extremely slim that you have used the function Gal_AddTimedTaskWithIO() in your own code; however, if you have, you must replace it with the appropriate version, depending on whether you're using it to poll files or sockets. For instance, the MITRE stdin polling utility had to be changed as follows:

Version 2.0

fd_set readfd;
unsigned int fd = fileno(stdin);
FD_ZERO(&readfd);
FD_SET(fd, &readfd);
Gal_AddTimedTaskWithIO(stdin_poll,
                       (void *) poll_struct,
                       poll_struct->ms,
                       &readfd, (fd_set *) NULL);

Version 2.1

Gal_AddTimedTaskWithFileIO(stdin_poll,
                           (void *) poll_struct,
                           poll_struct->ms,
                           stdin, (FILE *) NULL);


In addition, because select() doesn't support polling on file descriptors under Win32, the file I/O polling is not available on Win32.


Step 6: Upgrade your local Makefile (advanced)

If you don't use the C Makefile templates included in the distribution, and you opt for shared object compilation when you install Galaxy Communicator, you will have to update your references to the Galaxy Communicator libraries in your C Makefiles if you've chosen to encode library dependencies. We illustrate with a relevant subsection of the Makefile in our local Makefile example:

Version 2.0

LIBS = -L$(ROOTDIR)/lib/$(ARCHOS) -lGalaxy

LIBDEPS = $(ROOTDIR)/lib/$(ARCHOS)/libGalaxy.a

SOURCES =  ../double/double.c ../double/double_core.c

double: ../double/double.c $(LIBDEPS)
        gcc -g $(CPPFLAGS) -I../double -o double.$(ARCHOS).bin $(SOURCES) $(LIBS)

Version 2.1

LIBS = -L$(ROOTDIR)/lib/$(ARCHOS) -lGalaxy

# Depending on whether there are shared libraries or not,
# we want to pick the right one library extension, etc.

include $(TEMPLATES)/shared_libs.make

ifdef SHARED_LIBS
  ifdef XLINKER_RTFLAG
    LIBS += -Xlinker $(XLINKER_RTFLAG) -Xlinker $(ROOTDIR)/lib/$(ARCHOS)
  endif
endif

LIBDEPS = $(ROOTDIR)/lib/$(ARCHOS)/libGalaxy$(LIBEXT)

SOURCES =  ../double/double.c ../double/double_core.c

double: ../double/double.c $(LIBDEPS)
        gcc -g $(CPPFLAGS) -I../double -o double.$(ARCHOS).bin $(SOURCES) $(LIBS) -lm

The file templates/shared_libs.make is guaranteed to be created when you install and compile the Galaxy Communicator system, and it contains the appropriate setting for LIBEXT for you to use in other Makefiles, as well as the proper linker flag to pass via gcc which will set the run-time location of the shared library in the executable.
License / Documentation home / Help and feedback
Last updated August 10, 2000