![]() |
License / Documentation home / Help and feedback | ![]() |
For most applications, upgrading from 1.2 to 1.3 will be fairly transparent. The release notes should provide enough detail. However, the upgrade from 1.3 to 2.0 is much more complex. Here, we attempt to provide a step-by-step guide to updating your systems.
old | new |
FALSE | GAL_FALSE |
TRUE | GAL_TRUE |
BOOL | Gal_Boolean |
OA_CHAR | GAL_OA_CHAR |
OA_SHORT | GAL_OA_SHORT |
OA_INT | GAL_OA_INT |
OA_FLOAT | GAL_OA_FLOAT |
OA_STRING | GAL_OA_STRING |
OA_DOUBLE | GAL_OA_DOUBLE |
oa_check_usage | GalUtil_OACheckUsage |
oa_print_usage | GalUtil_OAPrintUsage |
oa_extract | GalUtil_OAExtract |
oa_extract_asserting | GalUtil_OAExtractAsserting |
SLS_VERBOSE | GAL_VERBOSE |
sls_fatal | GalUtil_Fatal |
sls_warn | GalUtil_Warn |
sls_error | GalUtil_Error |
sls_print | GalUtil_Print |
sls_cprint | GalUtil_CPrint |
sls_pinfo1 | GalUtil_PInfo1 |
sls_pinfo2 | GalUtil_PInfo2 |
sls_cpinfo1 | GalUtil_CPInfo1 |
sls_cpinfo2 | GalUtil_CPInfo2 |
sls_debug1 | GalUtil_Debug1 |
sls_debug2 | GalUtil_Debug2 |
sls_assert | GalUtil_Assert |
sls_verbose_use_bw | GalUtil_VerboseUseBW |
sls_verbose_use_color | GalUtil_VerboseUseColor |
Here's how the old style worked. For server declarations, the operations file contains the information about the server: server name, port, and operations. This file was examined at server compile time to determine which operations would be called. Based on the operations listed, a header file named "server.h" was automatically generated which mapped the name of each operation to the corresponding function in the server. Port was needed to set the default port (unlike the program file, host was unnecessary). Here's a sample operations file from the double server:
For dialogue control headers, the file "functions.h" was automatically generated which contained both the dialogue control function prototypes and a function map. The function prototypes were dictated by the operations listed in the dialogue control file rules. The function map mapped the function name to the actual function call. The location of the dialogue control file was declared in the Makefile using DCTL_FILE, and USE_DCTL was set to 1.
Variable | What it does | Obligatory? | Slot? |
OPERATIONS_FILE | The name of the operations file you need to define if you're using the old style of declaring server information. Defaults to galaxy/System/servers. | no | yes |
SFUNC.H | The name of the operations header file to generate, used for the old style of declaring server information. Defaults to server.h. | no | no |
SERVER_DATA | Value should be -server_data if you want to generate dispatch function signatures which contain an argument for the server structure. You should always use this if you're using the old style of declaring server information. | no, but use it anyway if you're using the old style | yes |
USE_DCTL | If the server uses the dialogue control mechanism, the value of this variable should be 1, using the old style of generating dialogue control headers. | no | yes |
DCTL_FILE | This variable specifies the dialogue control file for generating the dialogue function map, using the old style of generating dialogue control headers (the equivalent of server.h for the dialogue control mechanism). The default is galaxy/System/$(SERVER).dctl. | no | yes |
DFUNC.H | The name of the header file to generate for the dialogue function map, using the old style of generating dialogue control headers. Defaults to functions.h. | no | no |
In particular, the following important generalization applies:
The first of these changes in the distribution has to do with the second argument of dispatch functions. In GalaxyCommunicator 1.3 and previous, the server_data argument of dispatch functions was not reliably a connection object. In 2.0, this argument is guaranteed to be a GalSS_Environment * (the call environment of the dispatch function). All functions which handled this unreliability have been deprecated. See the section on adding a server.
In your dispatch functions, make the following modifications:
Note that parallel comments apply to the Python, Java and Common Lisp bindings.
In Python and Common Lisp, we've provided backward compatibility at the expense of a slight name misnomer; the Connection object is now actually a call environment object. If all you do is write frames to the Hub, access the server object and access the server object's output stream, these operations are all still transparently supported and your code should not need to change.
Note that the comments here apply only to dispatch functions. When you build a Communicator-compliant server, you have many opportunities to access the connection object for writing, etc. In addition to dispatch functions, you may set up another timed task whose callback function writes to the Hub (for instance, the MITRE stdin polling utility does this), or you might write a broker callback which writes to the Hub (as in the MITRE echo server). Even if you have access to a GalSS_Environment * object when you set up these callbacks, you cannot store this object away; as far as the programmer is concerned, the environment object does not persist past the scope of the dispatch function it is passed to. Instead, you must save away the connection object directly (accessible from the environment object via the GalSS_EnvComm() function. See the upgrade section on broker callbacks for a detailed comparison between 1.3 and 2.0 in such a case.
Because the previous logic was obscure, and people may not want to take the time to reconstruct the proper logic, we have introduced the function GalSS_EnvForceReturn, which explicitly forces its frame argument to be treated as the dispatch function return. All messages sent to the Hub after a forced return are treated as new messages.This function can be used in any dispatch function, not just in reinitialize; most significantly, however, it can be used as a simple explicit substitute for the dummy message send. We illustrate the mapping with the reinitialize dispatch function from double.c.
Old:
Gal_Frame reinitialize(Gal_Frame frame, void *server_data)Transparent substitution of GalSS_EnvForceReturn:
{
Gal_Frame new_f = Gal_MakeFrame("main", GAL_CLAUSE);/* Reply to reinitialize. */
GalSS_WriteFrameToHub(frame, server_data, 0);
/* New message. */
Gal_SetProp(new_f, ":int", Gal_IntObject(InitialIncrement));
return new_f;
}
Gal_Frame reinitialize(Gal_Frame frame, void *server_data)Corrected logic:
{
Gal_Frame new_f = Gal_MakeFrame("main", GAL_CLAUSE);/* Reply to reinitialize. */
GalSS_EnvForceReturn((GalSS_Environment *) server_data, f, 0);
/* New message. */
Gal_SetProp(new_f, ":int", Gal_IntObject(InitialIncrement));
return new_f;
}
Gal_Frame reinitialize(Gal_Frame frame, void *server_data)Note that parallel comments apply to the Python, Java and Common Lisp bindings.
{
Gal_Frame new_f = Gal_MakeFrame("main", GAL_CLAUSE);/* New message. */
Gal_SetProp(new_f, ":int", Gal_IntObject(InitialIncrement));
GalSS_EnvWriteFrame((GalSS_Environment *) server_data, new_f, 0);
Gal_FreeFrame(new_f);
return (Gal_Frame) NULL;
}
In Python and Common Lisp, the required changes are exactly parallel. You may either transparently force a return, or reorganize the logic to match other dispatch functions.
Old:
static void synth_data_handler(GalIO_BrokerStruct *broker_struct, void *data,New:
Gal_ObjectType data_type, int n_elements)
{
Gal_Frame fr;switch (data_type) {
case GAL_FRAME:
printf("Answer is: `%s'\n",
Gal_GetString((Gal_Frame) data, ":reply_string"));
GalIO_BrokerDataDone(broker_struct);/* Now send playing is done */
fr = Gal_MakeFrame("audio", GAL_CLAUSE);
Gal_SetProp(fr, ":playing_has_ended", Gal_IntObject(1));
GalSS_WriteFrameToHub(fr, (void *) NULL, 0);
Gal_FreeFrame(fr);
break;
default:
sls_warn("synth_data_handler: unexpected data type %s\n",
Gal_ObjectTypeString(data_type));
}
}Gal_Frame handle_synth_data(Gal_Frame frame, void *server_data)
{
char *host;
int port;
GalIO_BrokerStruct *b;host = (char *)Gal_GetValue(frame, ":synth_host", GAL_STRING);
port = (int)Gal_GetValue(frame, ":synth_port", GAL_INT);if (host && port) {
b = GalIO_BrokerDataInInit(host, port, frame,
synth_data_handler, server_data, 0);
if (b) GalIO_SetBrokerActive(b);
}
return frame;
}
static void synth_data_handler(GalIO_BrokerStruct *broker_struct, void *data,Previously, the argument passed to the dispatch function was the server argument or NULL; now it is guaranteed to be a GalSS_Environment *. However, the environment object is reclaimed after the dispatch function is called, and as a result it can't be passed to the broker callback function to be stored. So the right thing to do is to store the actual connection object, accessed with the GalSS_EnvComm function, and send a message from the broker callback using GalIO_CommWriteFrame. If you want to access connection or server data, the lesson is the same: store the actual connection object and use GalIO_GetCommServerData and GalIO_GetCommData.
Gal_ObjectType data_type, int n_elements)
{
Gal_Frame fr;switch (data_type) {
case GAL_FRAME:
printf("Answer is: `%s'\n",
Gal_GetString((Gal_Frame) data, ":reply_string"));
GalIO_BrokerDataDone(broker_struct);/* Now send playing is done */
fr = Gal_MakeFrame("audio", GAL_CLAUSE);
Gal_SetProp(fr, ":playing_has_ended", Gal_IntObject(1));
GalIO_CommWriteFrame((GalIO_CommStruct *) GalIO_GetBrokerCallerData(broker_struct), fr, 0);
Gal_FreeFrame(fr);
break;
default:
GalUtil_Warn("synth_data_handler: unexpected data type %s\n",
Gal_ObjectTypeString(data_type));
}
}Gal_Frame handle_synth_data(Gal_Frame frame, void *server_data)
{
char *host;
int port;
GalIO_BrokerStruct *b;host = (char *)Gal_GetValue(frame, ":synth_host", GAL_STRING);
port = (int)Gal_GetValue(frame, ":synth_port", GAL_INT);if (host && port) {
b = GalIO_BrokerDataInInit(host, port, frame,
synth_data_handler,
GalSS_EnvComm((GalSS_Environment *) server_data), 0);
if (b) GalIO_SetBrokerActive(b);
}
return frame;
}
Note that parallel comments apply to the Python, Java and Common Lisp bindings.
In Python and Common Lisp, the broker data must be passed the connection object, but the dispatch function argument is now a call environment object instead. You must access the connection object from it. See the brokering examples in MITRE's audio example.
Old:
static Gal_Frame prepare_audio_frame(char *filename)New:
{
/* .... *//* Now that we have the audio, we add a binary element. */
/* These must be added to the broker frame BEFORE the
broker object is initialized. */
sprintf(host_pid, "%s:%d", GalIO_IPAddress(), (int) getpid());
Gal_SetProp(f, ":call_id", Gal_StringObject(host_pid));
b = GalIO_BrokerDataOutInit(3900, f, 0, 0);
if (b && (Gal_GetInt(f, ":broker_port") > 0)) {
Gal_SetProp(f, ":binary_host", Gal_StringObject(GalIO_IPAddress()));
Gal_SetProp(f, ":binary_port",
Gal_IntObject(Gal_GetInt(f, ":broker_port")));
GalIO_BrokerWriteString(b, AUDIO_START);
GalIO_BrokerWriteBinary(b, buf, total);
GalIO_BrokerWriteString(b, AUDIO_END);
GalIO_BrokerDataOutDone(b);
}
return f;
}
static Gal_Frame prepare_audio_frame(char *filename)
{
/* .... *//* Now that we have the audio, we add a binary element. */
/* These must be added to the broker frame BEFORE the
broker object is initialized. */
sprintf(host_pid, "%s:%d", GalIO_IPAddress(), (int) getpid());
GalIO_FrameSetBrokerCallID(f, host_pid);
b = GalIO_BrokerDataOutInit(3900, f, 0, 0);
if (b && (GalIO_FrameGetBrokerPort(f) > 0)) {
Gal_SetProp(f, ":binary_host", Gal_StringObject(GalIO_IPAddress()));
Gal_SetProp(f, ":binary_port",
Gal_IntObject(GalIO_FrameGetBrokerPort(f)));
GalIO_BrokerWriteString(b, AUDIO_START);
GalIO_BrokerWriteBinary(b, buf, total);
GalIO_BrokerWriteString(b, AUDIO_END);
GalIO_BrokerDataOutDone(b);
}
return f;
}
Old:
int main(int argc, char **argv)New:
{
GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) NULL;gcr = (GalaxyCallbackRecord *) malloc(sizeof(GalaxyCallbackRecord));
gcr->timer_cb = (TimerCallback *) NULL;
gcr->server_sock = -1;
gcr->client_sock = -1;
gcr->l = SM_NewLooper();Gal_InitializeServerDefaults(ServerName, DefaultPort, SvrFunctionMap);
gcr->gcomm = GalIO_ServerInit(Gal_GetServerPort(), 0,
GalSS_FrameHandler, (void *) gcr, -1);
if (!gcr->gcomm) {
fprintf(stderr, "Couldn't create a server\n");
fflush(stderr);
free(gcr->l);
free(gcr);
exit(1);
}
gcr->server_sock = GalIO_GetCommListenSocket(gcr->gcomm);
SM_AddFDCallback(gcr->l, gcr->server_sock, DoubleServerHandler,
(void *) gcr);
SM_Mainloop(gcr->l);
exit(0);
}
int main(int argc, char **argv)Observe the following differences:
{
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);
free(gcr->l);
free(gcr);
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);
}
Old:
static void DoubleServerHandler(void *client_data)New:
{
int status;
GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) client_data;status = GalIO_ServerHandler(gcr->gcomm);
if ((status != -1) && (gcr->client_sock == -1)) {
gcr->client_sock = GalIO_GetCommSocket(gcr->gcomm);
SM_AddFDCallback(gcr->l, gcr->client_sock, DoubleConnectionHandler,
(void *) client_data);
}
}
status = GalIO_ServerHandler(gcr->gcomm, &new_conn);
switch (status) {
case 1:
/* We've got a connection. */
client_sock = GalIO_GetCommSocket(new_conn);
GalIO_SetCommData(new_conn, gcr, NULL);
SM_AddFDCallback(gcr->l, client_sock, DoubleConnectionHandler,
(void *) new_conn);
break;
case -1:
/* An error has occurred. */
GalUtil_Warn("The server has failed.\n");
SM_RemoveAllFDCallbacks(gcr->l);
GalIO_SetServerDone(gcr->gcomm);
GalIO_DestroyServerStruct(gcr->gcomm);
break;
default:
break;
}
}
Old (from timer_double.c):
static void DoubleHandler(void *client_data)New:
{
GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) client_data;if (!gcr->server_connected) {
switch (GalIO_ServerHandler(gcr->gcomm)) {
case 1:
gcr->server_connected = 1;
}
} else {
switch (GalIO_ConnectionPoll(gcr->gcomm)) {
case 1:
/* Shut down the server */
SM_RemoveTimerCallback(gcr->l, gcr->timer_cb);
gcr->server_sock = -1;
gcr->server_connected = 0;
gcr->client_sock = -1;
break;
case -1:
/* the connection is done */
gcr->server_connected = 0;
gcr->client_sock = -1;
break;
}
}
}
static void DoubleHandler(void *client_data)The new function GalIO_ServerPoll takes care of polling all the server's connections if the timed task loop is not enabled. You could also use this function in the file descriptor case, but it's less efficient.
{
GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) client_data;GalIO_ServerPoll(gcr->gcomm);
}
Old:
static void DoubleConnectionHandler(void *client_data)New:
{
GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) client_data;/* GalIO_ConnectionPoll returns 1 when the server
and client should be terminated, -1 when the
client should be terminated. */
switch (GalIO_ConnectionPoll(gcr->gcomm)) {
case 1:
/* Server disconnected */
SM_RemoveFDCallback(gcr->l, gcr->server_sock);
SM_RemoveFDCallback(gcr->l, gcr->client_sock);
gcr->client_sock = -1;
gcr->server_sock = -1;
gcr->gcomm = (GalIO_CommStruct *) NULL;
break;
case -1:
/* Client disconnected */
SM_RemoveFDCallback(gcr->l, gcr->client_sock);
gcr->client_sock = -1;
break;
}
}
static void DoubleConnectionHandler(void *client_data)Observe the following differences:
{
GalIO_CommStruct *new_conn = (GalIO_CommStruct *) client_data;
GalaxyCallbackRecord *gcr = (GalaxyCallbackRecord *) GalIO_GetCommData(new_conn);
int fd = GalIO_GetCommSocket(new_conn);/* GalIO_ConnectionPoll returns 1 or -1 when the client
should be terminated. */
switch (GalIO_ConnectionPoll(new_conn)) {
case 1:
/* Done, stop polling. */
case -1:
/* Error, stop polling. */
SM_RemoveFDCallback(gcr->l, fd);
break;
default:
break;
}
}
Old:
void *_GalSS_init_server(GalIO_ServerStruct *s, int argc, char **argv)New:
{
char *prompt;
int i;
char *prog_name;
MGal_StdinPoll *poll_obj;if (!oa_check_usage(argc, argv, OAS, &i))
exit(1);
if (!oa_extract(argc, argv, OAS, "-prompt", OA_STRING, &prompt))
prompt = DefaultPrompt;
if (!oa_extract(argc, argv, OAS, "-prog_name", OA_STRING, &prog_name))
prog_name = DefaultProgram;poll_obj = MGalSS_CreateStdinPoll(prompt,
(void *) s, CreateTextFrame, 500, 0);
MGal_SetStdinPollData(poll_obj, (void *) prog_name);return (void *) poll_obj;
}Gal_Frame reinitialize(Gal_Frame f, void *server_data)
{
if (Gal_GetObject(f, ":greeting")) {
sls_print(-1, "[Greeting] %s\n", Gal_GetString(f, ":greeting"));
}
/* set up poll for stdin */
MGal_ActivateStdinPoll((MGal_StdinPoll *) GalSS_GetServerData(server_data));
return f;
}
void *_GalSS_init_server(GalIO_ServerStruct *s, int argc, char **argv)Observe the following differences:
{
int i;if (!GalUtil_OACheckUsage(argc, argv, OAS, &i))
exit(1);
if (!GalUtil_OAExtract(argc, argv, OAS, "-prompt", GAL_OA_STRING, &Prompt))
Prompt = DefaultPrompt;
if (!GalUtil_OAExtract(argc, argv, OAS, "-prog_name", GAL_OA_STRING, &ProgName))
ProgName = DefaultProgram;
GalIO_SetServerMaxConnections(s, 1);
return (void *) NULL;
}Gal_Frame reinitialize(Gal_Frame f, void *server_data)
{
GalSS_Environment *env = (GalSS_Environment *) server_data;if (Gal_GetObject(f, ":greeting")) {
GalUtil_Print(-1, "[Greeting] %s\n", Gal_GetString(f, ":greeting"));
}GalSS_EnvSetCommData(env,
(void *) MGalIO_CreateStdinPoll(Prompt, GalSS_EnvComm(env),
CreateTextFrame, 500, 0), FreePoll);
MGal_SetStdinPollData(GalSS_EnvGetCommData(env), (void *) ProgName);
/* set up poll for stdin */
MGal_ActivateStdinPoll((MGal_StdinPoll *) GalSS_EnvGetCommData(env));
return f;
}
![]() |
License / Documentation home / Help and feedback | ![]() |