![]() |
License / Documentation home / Help and feedback | ![]() |
void *_GalSS_init_server(GalIO_ServerStruct *server, int argc, char **argv)
Each Communicator-compliant server accepts the following arguments:
static char *oas[] = {If both -port and -contact_hub are present, the server will start up as both client and listener. For more details on the -server_locations argument, see the server locations documentation.
"-port port", "run as a server, listening for client connections on this port", NULL,
"-assert", "exit unhappily if we fail to get the desired port",
"-color", "enable color printing in a cxterm",
#ifdef _REENTRANT
"-thread", "use threads instead of timed tasks (experimental)",
#endif /* _REENTRANT */
"-nottloop", "do not use the timed_tasks_loop mechanism",
"-ttloop", "obsolete -- maintained for compatibility only",
"-maxconns max", "maximum number of connections for ttloop", "1",
"-validate", "validate each message send and return against the message signature",
"-verbosity level", "set the verbosity of this server", NULL,
"-contact_hub \"host:port...\"", "run as client, contacting Hubs at the specified host and port pairs (overrides default port setting, but not -port)", NULL,
"-server_locations_file file", "a server locations file of lines server host:port [hub|server]", NULL,
"-slf_name name", "if -server_locations_file is used, optional file index", NULL,
"-session_id id", "if -contact_hub is used, lock this client to the specified Hub session", NULL,
NULL
};
See the oa library for details on how these flags are processed. These arguments will be removed from the arglist before it is passed to _GalSS_init_server. For more details on how to run this server, see the section on the server executables.
When the server is first run, the generic server library will initialize the server by calling _GalSS_init_server. If the function is not defined, the default in the library will be used. The argument of this function is a GalIO_ServerStruct *. This is the server structure which maintains information about the default listen port, the dispatch functions, the server name, the number of connections, etc. For more details about what functions can be used to manipulate the server object, see the server architecture documentation.
The _GalSS_init_server function in double_core.c sets the initial increment, extracted from the command line.
static char *oas[] = {
"-increment i", "set the initial value", "1",
NULL
};
void *_GalSS_init_server(GalIO_ServerStruct *s, int argc, char **argv)Returning a non-NULL value from _GalSS_init_server has the same effect as calling GalIO_SetServerData, although using this capability is strongly discouraged.
{
int i, increment = 1;if (!GalUtil_OACheckUsage(argc, argv, oas, &i))
exit(1);
GalUtil_OAExtract(argc, argv, oas, "-increment", GAL_OA_INT, &increment);
GalIO_SetServerData(s, (void *) increment, NULL);
return (void *) NULL;
}
void _GalSS_print_usage(int
argc, char **argv)
This is one way for the server to print out its usage information. The
default way of defining this function is
void _GalSS_print_usage(int argc, char **argv)The function GalUtil_OAPrintOptions is provided in connection with the oa library, and can be used when the appropriate argument array is defined. See the example at the beginning of this section.
{
GalUtil_OAPrintOptions(argc, argv, oas, NULL);
printf("\n");
}
It's important that every reply to a dispatch function include the administrative information associated with that call. The various ways of providing a dispatch function reply all guarantee that this information will be included (returning a frame from a dispatch function, as well as calling the functions GalSS_EnvDestroyToken, GalSS_EnvError, or GalSS_EnvReply, all of which we'll discuss in a moment). The call environment is also used to send new messages, using the functions GalSS_EnvWriteFrame and GalSS_EnvDispatchFrame.
Although the call environment is almost always the proper channel through which to communicate with the Hub, the actual communication is done via the connection object, which is stored in the call environment and is accessible via the function GalSS_EnvComm.
For more details about functions which manipulate the connection object, see the server architecture documentation. For more details on the use and manipulation of call environments, see the environment management and session management documentation.
multiply.c defines a dispatch function, multiply, which multiplies a number in the :int slot by a predefined factor and returns the number.
Gal_Frame multiply(Gal_Frame frame, void *server_data)If you need to cache information per connection across calls to dispatch functions, it is possible to set and get connection-specific data directly through the environment object.
{
int i = Gal_GetInt(frame, ":int");if ((INT_MAX / i) < Factor) {
/* If we're about to overflow... */
GalSS_EnvError((GalSS_Environment *) server_data,
"multiply would overflow MAXINT");
return (Gal_Frame) NULL;
} else {
Gal_SetProp(frame, ":int",
Gal_IntObject(Factor * i));
return frame;
}
}Memory management
Both the frame which is passed to the dispatch function and the frame which is returned (if different) are freed by the toplevel loop using Gal_FreeFrame. No dispatch function should free the incoming frame. See the frame documentation for detailed memory management comments about frames.
void *GalSS_EnvGetCommData(GalSS_Environment
*env)
Retrieves the connection-specific data from the connection object stored
in env. See also GalIO_GetCommData.
void GalSS_EnvSetCommData(GalSS_Environment
*env, void *data, void (*free_fn)(void *))
Sets the data specific to the connection stored in env.. If free_fn
is non-NULL, it will be called on the data when the data is reset
or the connection is destroyed. See also GalIO_SetCommData.
If you've cached information specific to a given dispatch function, it
is possible to access this information directly through the environment
object.
void *GalSS_EnvGetClientData(GalSS_Environment
*env, const char *name)
Retrieves the information specific to the dispatch function name
from the connection object stored in env. See also GalIO_GetCommClientData.
You should always use the call environment to send these messages. It is possible to send them using the connection alone (accessible via the call environment using the function GalSS_EnvComm(); see the server structure documentation for details), but important session information will be lost. When you're inside a dispatch function, using the call environment is straightforward; if you're outside a call environment (if you've set up a brokering callback or a timed task), you must take special precautions to make sure the call environment isn't freed after the dispatch function exits. See the environment management documentation for more details.
When a message is written to or read from the Hub, the type of the message
is also specified. The possible types are:
GAL_MESSAGE_MSG_TYPE | A new message |
GAL_REPLY_MSG_TYPE | A normal reply to a message |
GAL_ERROR_MSG_TYPE | An error reply to a message |
GAL_DESTROY_MSG_TYPE | A destroy request for the specified token (also counts as a message reply) |
GAL_POSTPONE_MSG_TYPE | A reply from the server informing the Hub that the message return will be provided later (see the documentation on continuations) |
If the message type is GAL_ERROR_MSG_TYPE, the accompanying frame will currently have the following form (use GalIO_GetError to access the values):
{c system_errorAt this point, possible errors are:
:errno <num>
:err_description <string>
...}
GAL_APPLICATION_ERROR | Some server generated an error via GalSS_EnvError |
GAL_TRANSMISSION_ERROR | An error was encountered in sending a frame |
GAL_RECEPTION_ERROR | An error was encountered in reading a frame |
GAL_NO_OPNAME_ERROR | Some server did not implement a requested operation |
GAL_SERVER_DOWN_ERROR | The Hub could not contact a required server |
GAL_NO_FRAME_ERROR | A dispatch was requested on an empty frame |
int GalSS_EnvWriteFrame(GalSS_Environment
*env, Gal_Frame frame, int do_block)
This function writes frame to the Hub through the connection
object stored in the env. If do_block is set, the function
guarantees that the write has happened before returning. The message type
written is always GAL_MESSAGE_MSG_TYPE.
double_core.c defines a dispatch function, twice, which takes the value of :int, doubles it, introduces a new message to the Hub with the doubled integer, and returns NULL. If it is about to overflow MAX_INT, or if the number it's supposed to double is 0, then it generates an error.
if (!program_name)
program_name = "main";
if (i == 0) {
/* We'll loop forever. */
GalSS_EnvError((GalSS_Environment
*) server_data,
"i is 0");
return (Gal_Frame) NULL;
} else if ((INT_MAX / i) < 2) {
/* If we're about to overflow... */
GalSS_EnvError((GalSS_Environment
*) server_data,
"double would overflow MAXINT");
return (Gal_Frame) NULL;
} else {
new_f = Gal_MakeFrame(program_name,
GAL_CLAUSE);
Gal_SetProp(new_f,
":int", Gal_IntObject(2 * i));
Gal_SetProp(new_f,
":program", Gal_StringObject(program_name));
GalSS_EnvWriteFrame((GalSS_Environment
*) server_data, new_f, 0);
Gal_FreeFrame(new_f);
return (Gal_Frame) NULL;
}
}
Gal_Frame GalSS_EnvDispatchFrame(GalSS_Environment
*env, Gal_Frame frame, GalIO_MsgType *t)
This function implements a server-to-server subdialogue with the Hub.
It sends the frame through the connection object stored in the env
and waits for a reply. This function is implemented in terms of GalIO_DispatchViaHub.
The message type written is always GAL_MESSAGE_MSG_TYPE. The type of the
reply is stored in t. The only values for *t you'll ever see
are *t are GAL_REPLY_MSG_TYPE and GAL_ERROR_MSG_TYPE; for all others,
a warning message is printed and NULL is returned.
See also GalSS_EnvDispatchFrameToProvider, GalSS_EnvDispatchFrameWithContinuation and GalSS_EnvDispatchFrameToProviderWithContinuation.
int GalIO_GetError(Gal_Frame
f, char **err_desc)
Retrieves an error from a error frame returned from GalSS_EnvDispatchFrame.
If the frame is not a well-formed error frame, this function will return
-1 and set *err_desc to NULL if err_desc is provided. If the
frame is a well-formed error frame, this function will return an error code, and set *err_desc if
err_desc is provided. If an error description is present in the
frame, the description will be a string, otherwise NULL.
double_core.c defines a dispatch function, complex_twice, which doubles the result it gets from the multiply server:
Gal_Frame complex_twice(Gal_Frame frame, void *server_data)
{
Gal_Frame new_f = Gal_MakeFrame("multiply", GAL_CLAUSE);
Gal_Frame res_f;
GalIO_MsgType t;
int i;Gal_SetProp(new_f, ":int", Gal_IntObject(Gal_GetInt(frame, ":int")));
res_f = GalSS_EnvDispatchFrame((GalSS_Environment *) server_data, new_f, &t);
Gal_FreeFrame(new_f);if (!res_f) {
GalUtil_Warn("Didn't hear back from multiply");
return (Gal_Frame) NULL;
}switch (t) {
case GAL_REPLY_MSG_TYPE:
prog_name = Gal_GetString(frame, ":program");
if (!prog_name) prog_name = "main";
Gal_SetProp(new_f, ":program", Gal_StringObject(program_name));
return twice(res_f, server_data);
case GAL_ERROR_MSG_TYPE:
return (Gal_Frame) NULL;
default:
return (Gal_Frame) NULL;
}
}
Note that it is also possible to postpone the reply to a message beyond the scope of the dispatch function. The functions for doing this and the motivation for it are discussed extensively in the documentation about continuations.
int GalSS_EnvError(GalSS_Environment
*env, const char *description)
Reports an error as the return value. The message type of the message
written is always GAL_ERROR_MSG_TYPE; it is annotated with the administrative
information of the frame passed in. If you use this function, the return
value from the dispatch function will be ignored.
int GalSS_EnvDestroyToken(GalSS_Environment
*env)
Makes a destroy request. The token information is taken from the frame
passed in. The message type of the message written is always GAL_DESTROY_MSG_TYPE.
If you use this function, the return value from the dispatch function will
be ignored.
int GalSS_EnvReply(GalSS_Environment
*env, Gal_Frame f)
Provides the frame f as a normal reply of type GAL_REPLY_MSG_TYPE.
This function is provided mostly for completeness, since it duplicates the
functionality embodied in returning a frame from the dispatch function.
However, if for some reason you want a dispatch function to keep running
past its "natural" reply point, you can use this function to satisfy the Hub's
expectation of a reply.
See also GalSS_EnvPostponeReply.
Gal_Frame reinitialize(Gal_Frame frame, void *server_data);
The dispatch function reinitialize is special. It is called when the Hub first connects to the server. Any connection-specific initializations should be put in the reinitialize dispatch function. Like all other dispatch functions, the second argument is typed to void * for backward compatibility, but can reliably be cast to GalSS_Environment *.
As of version 3.0, reinitialize is no longer restricted in its use. However, because it is used to initialize a connection, it's worth observing that while is convenient to use the reinitialize message to "seed" the Hub by providing a new token via GalSS_EnvWriteFrame(), GalSS_EnvDispatchFrame should not be used, for three very good reasons:
frame key | source | type | default value |
<frame name> | (specified in code; cannot be changed) | string | reinitialize |
:server_type | the name of the server as the Hub knows it, if not already specified | string | <none> |
Variable | What it does | Obligatory? | Slot? |
MAKEFILE | Declares the name of the file, in order to generate the Makefile dependencies correctly | yes | yes |
ROOT_DIR | This is the root of the Communicator distribution | yes | yes |
CPPFLAGS | The usual C preprocessor flags. | no | yes |
LDFLAGS | The usual C linker flags. | no | yes |
COMMON_LIBS | Defined here and augmented in templates/Galaxy.make.
Third party libraries without variants. |
no | yes |
SPECIAL_LIBS, SPECIAL_LIBS_D, SPECIAL_LIBS_P |
Libraries with variants (_debug, _profile) which are particular to the current executable. If any of the variants are non-standard, set SPECIAL_LIBS_D and/or SPECIAL_LIBS_P. These will be arguments to the link line. | no | yes |
LIBDEPS LIBDEPS_D, LIBDEPS_P |
A list of libraries, which will be Makefile dependences (so real pathnames, not -l values). These should correspond to the special and common libraries. | no | yes for LIBDEPS |
LIBTARGET, EXECTARGETS, SERVER, APPLET |
The primary target types. Exactly one of these must be set. The SERVER is the option for building a Communicator-compliant server which automaticaly generates the operations header file. | yes | yes |
LIBDIR | The directory to put a library in. Defaults to $(ROOT_DIR)/lib/. Note the trailing slash. The library will be put in the appropriate $(ARCHOS) subdirectory. We advise setting this so as not to write to the Communicator distribution. | no | no |
EXECDIR | The directory to put an executable or server in. Defaults to $(ROOT_DIR)/bin/. Note the trailing slash. The executable or server will be put in the appropriate $(ARCHOS) subdirectory. We advise setting this so as not to write to the Communicator distribution. | no | no |
THREAD_SAFE | Whether the server or library supports thread-safe compilation. If you uncomment this line, the compilation instruction "make thread" will compile a threaded version of the library or server. If you leave this line uncommented, this instruction will compile the normal version. See the thread notes. | no | yes |
SUBDIRS | A list of subdirectories where some of the sources may be. | no | yes |
SOURCES | The source files. | yes | yes |
GAL_SERVER_NAME(double)Here's a version of the double server declaration file using the extended signatures:
GAL_SERVER_PORT(2800)
GAL_SERVER_OP(twice)
GAL_SERVER_OP(complex_twice)
GAL_SERVER_OP(reinitialize)
GAL_SERVER_NAME(double)The extended version which specifies the signature works as follows:
GAL_SERVER_PORT(2800)
GAL_SERVER_OP_SIGNATURE(twice,
GAL_SERVER_OP_KEYS(":int" _ GAL_INT _ GAL_KEY_ALWAYS),
GAL_OTHER_KEYS_NEVER,
GAL_REPLY_NONE,
NULL,
GAL_OTHER_KEYS_NEVER)
GAL_SERVER_OP_SIGNATURE(complex_twice,
GAL_SERVER_OP_KEYS(":int" _ GAL_INT _ GAL_KEY_ALWAYS),
GAL_OTHER_KEYS_NEVER,
GAL_REPLY_NONE,
NULL,
GAL_OTHER_KEYS_NEVER)
GAL_SERVER_OP_SIGNATURE(reinitialize,
NULL,
GAL_OTHER_KEYS_NEVER,
GAL_REPLY_PROVIDED,
NULL,
GAL_OTHER_KEYS_NEVER)
argument | description | legal values |
dispatch_fn | the name of a dispatch function | |
in_keys | the keys which this dispatch function expects | Either NULL or a declaration GAL_SERVER_OP_KEYS(key _ type _ obligatory ... ), where underscores are used in place of commas because of the idiosyncracies of the C preprocessor. Here key is a string, type is a legal object type, and obligatory is either GAL_KEY_ALWAYS or GAL_KEY_SOMETIMES. This declaration may contain any number of key declarations (that is, its arguments must be a multiple of 3, all separated by underscores). The type GAL_FREE is used as a wildcard match. |
allow_other_in_keys | whether or not the list of in_keys provided is complete | either GAL_OTHER_KEYS_MAYBE or GAL_OTHER_KEYS_NEVER |
reply_status | whether or not the dispatch function returns anything | one of GAL_REPLY_PROVIDED, GAL_REPLY_NONE, GAL_REPLY_UNKNOWN |
out_keys | the keys which this dispatch function returns | (see in_keys) |
allow_other_out_keys | whether or not the list of out_keys provided is complete | (see allow_other_in_keys) |
These signatures can be used to validate calls to the dispatch functions if you pass the -validate flag to the server, and will be used in the future to pass to the Hub and synchronize signatures.
Here is the header text from double.c:
#include "galaxy/galaxy_all.h"This technique for providing server information is compatible with non-MIT Makefiles (as well as being cross-platform-friendly). If not using the MIT Makefile templates to build your servers, be sure that you have -I. on your compile line to ensure that the compiler can find the declaration file.
#define SERVER_FUNCTIONS_INCLUDE "double_server.h"
#include "galaxy/server_functions.h"
Note: the value of SERVER_FUNCTIONS_INCLUDE is included multiple times in server_functions.h. Therefore, the usual header programming practice of ensuring the header is loaded only once will cause the declaration macros to fail. In other words, don't do this:
#ifndef __MY_SERVER_H__
#define __MY_SERVER_H__GAL_SERVER_NAME(my_server)
...
#endif
/* We don't need any signatures because the functions have already been defined. */Note: if you do this in a C++ application, you will probably need to prefix extern "C" to the function definition to defeat C++ name mangling, since the infrastructure expects to find a function with this exact name.void _GalSS_InitializeDefaults(GalIO_ServerStruct *s)
{
GalSS_InitializeServerDefaults(s, "double", 2800);
GalSS_AddDispatchFunction(s, "twice", twice, NULL,
GAL_OTHER_KEYS_MAYBE, GAL_REPLY_UNKNOWN,
NULL, GAL_OTHER_KEYS_MAYBE);
GalSS_AddDispatchFunction(s, "complex_twice", complex_twice, NULL,
GAL_OTHER_KEYS_MAYBE, GAL_REPLY_UNKNOWN,
NULL, GAL_OTHER_KEYS_MAYBE);
GalSS_AddDispatchFunction(s, "reinitialize", reinitialize, NULL,
GAL_OTHER_KEYS_MAYBE,
GAL_REPLY_UNKNOWN,
NULL, GAL_OTHER_KEYS_MAYBE);
}
The following typedefs are relevant to the functions below:
typedef Gal_Frame (*Gal_FrameFnPtr)(Gal_Frame frame);
typedef Gal_Frame (*Gal_FrameDataFnPtr)(Gal_Frame frame, void *data);
void GalSS_InitializeServerDefaults(GalIO_ServerStruct
*scomm, char *name, unsigned short port)
Stores the name and port and function_map in the
server scomm. The name corresponds to a Hub service type.
void GalSS_AddDispatchFunction(GalIO_ServerStruct
*i, const char *name, Gal_FrameDataFnPtr fn, Gal_DispatchFnSignatureKeyEntry
*in_key_array, int allow_other_in_keys, int reply_provided,
Gal_DispatchFnSignatureKeyEntry *out_key_array, int allow_other_out_keys)
Adds a fn indexed by name to the server i. This
function takes a frame and a void * pointer (actually a GalSS_Environment
*) and returns a frame. By convention, the index and the name of the
function are the same, and that's the way the header strategy implements
things, but this is not required (as long as the programmer is willing to
assume responsibility for negotiating the digression). The possible values
of allow_other_in_keys, reply_provided, and allow_other_out_keys are the
same as for the GAL_SERVER_OP_SIGNATURE() macro. The in_key_array and out_key_array
are created by the function Gal_CreateDispatchFnKeyArray().
Gal_DispatchFnSignatureKeyEntry *Gal_CreateDispatchFnKeyArray(int
ignore, ... )
This function takes an arbitrary number of arguments. The first argument
is ignored; it is present because the ANSI C mechanism for variable arguments
requires at least one listed argument. The argument list must terminate
with a NULL; otherwise, it's identical to the arguments to the GAL_SERVER_OP_KEYS()
macro. Here's an example:
Gal_CreateDispatchFnKeyArray(0, ":int", GAL_INT, GAL_KEY_ALWAYS, NULL);void Gal_FreeDispatchFnKeyArray(Gal_DispatchFnSignatureKeyEntry *entry)Memory management
The function GalSS_AddDispatchFunction copies the key array, so if you create an array to pass to GalSS_AddDispatchFunction, you must free it using Gal_FreeDispatchFnKeyArray.
SERVER: double-serverThis entry instructs the Hub to contact the server in question on port 2800 on the local machine, and informs the Hub that the server supports a dispatch function named twice. (It's also possible to have the server contact the Hub instead.) In addition, you might want to tell the Hub what to do with messages the server sends to it:
PORT: 2800
HOST: localhost
OPERATIONS: twice
RULE: :int --> twice
IN: :int
OUT: none!
#include "galaxy/galaxy_all.h"The value of GC_VERSION is a number in hexadecimal form, such that the lowest two hex digits are the "subminor" version, the next two hex digits are the minor version, and everything above that is the major version. So Galaxy Communicator version 4.2.3 (if we ever get there) will have a GC_VERSION of 0x40203.#if defined(GC_VERSION) && (GC_VERSION >= 0x30000)
char *version = "3 point 0";
#else
#if defined(GC_VERSION) && (GC_VERSION >= 0x20000)
char *version = "between 2 point 0 and 3 point 0";
#else
#ifndef GC_VERSION
char *version = "before 2 point 0";
#endif
#endif
#endif
![]() |
License / Documentation home / Help and feedback | ![]() |