Galaxy Communicator Documentation:

Saving Away An Environment Object For Later Use

License / Documentation home / Help and feedback

Let's say you want to write a frame to the Hub, but you don't want to do it from a dispatch function. You need an environment to use, so you save a reference to the environment from within a dispatch function. But later, when you try to write your frame, your application crashes. What went wrong? The answer lies in the memory management rules for environment objects, which is the topic of this section.


What's the problem?

As we saw in the tutorial, the call environment instantiates the information required to communicate properly with the Hub. Much of the time, this communication happens from within a dispatch function, which is where call environments are found:
Gal_Frame do_something(Gal_Frame f, void *server_data)
{
  GalSS_Environment *env = (GalSS_Environment *) server_data;
  Gal_Frame new_f;

  /* ... */
  GalSS_EnvWriteFrame(env, new_f, 0);
  /* ... */
}

(We talk about this in more detail in the documentation on adding a server.) These call environments can be used inside dispatch functions to provide replies or send new messages to the Hub. But dispatch functions aren't restricted to providing a reply to an incoming message or sending new messages; they can also set up callbacks or timed tasks which may send new messages later. So the programmer might want to save away a call environment to send a message outside the scope of the dispatch function. However, if you're not careful, you can crash your program doing this. The reason is that the call environment is allocated for the call to the dispatch function, and freed (under normal circumstances) when the dispatch function exits. So if you touch it later, it'll be gone.

In order to address this issue, we've set up the call environment so that it's managed by a reference count. When the reference count reaches 0, the call environment is freed. As a programmer, there are a number of ways you can exploit this feature to ensure that the environment will be around when you need it.


Functions which save away environments for you

There are a number of circumstances where you might logically want to save away a call environment. These include broker callbacks and timed tasks. In order to make your job easier, we provide utilities for cleanly saving a call environment in all the callback contexts the infrastructure supports.

In particular, the Galaxy Communicator library provides environment-aware broker callback and timed task setup, as well as a way of maintaining an environment through successive calls to dispatch functions. In this section, we describe some of these functions.

void GalSS_TaskSetEnvironment(Gal_TaskPkg *p, GalSS_Environment *env)
Sets the environment associated with the task p to env.

GalSS_Environment *GalSS_TaskGetEnvironment(Gal_TaskPkg *p)
Returns the environment associated with the task p.

Gal_TaskPkg *GalSS_EnvAddTask(GalSS_Environment *env, void (*task)(Gal_TaskPkg *), void *caller_data, long num_millisecs, int read_blocking_available, GAL_SOCKET *read_socket, GAL_SOCKET *write_socket, GAL_SOCKET *err_socket, FILE *read_file, FILE *write_file, FILE*err_file, Gal_TaskConditionFn condition, void (*cleanup_fn)(void *))
This function provides all the functionality of Gal_AddTaskExtended, but also saves away the call environment env for access using the function GalSS_TaskGetEnvironment. A task thus established can be refired in any of the normal ways; see the timed task documentation for details.

At times, connections may be associated with UI elements, which can issue new messages. So the UI itself is an element outside the scope of dispatch functions where it would be helpful to have a call environment to use to issue new messages. It's not enough to set up the environment when the connection is established; crucial properties of the UI interaction, such as the session ID, may be changed by other servers in the course of evaluation. The safest thing to do is to create an environment for the UI when the connection is established, update it every time a dispatch function is called, and free the environment when the connection is shut down. The function GalSS_EnvMaintainInLocation does this for you.

void GalSS_EnvMaintainInLocation(GalIO_CommStruct *gcomm, const char *initial_session_id, GalSS_Environment **env_loc)
The connection gcomm is the connection to the Hub which is associated with a UI element for the current session, and hosts the dispatch functions which are fired. This function seeds the location env_loc with a new environment and updates its session ID to initial_session_id. It sets a dispatch function callback via GalIO_AddConnectionDispatchFnCallback to keep the location current, and sets up a shutdown callback to free the environment.


Saving away environments by hand

There are a number of functions which store away an environment for later use in a callback, for example GalSS_EnvAddTask and GalIO_EnvBrokerDataInInit. These functions increment a reference count associated with the environment, so that the environment isn't freed prematurely. However, there may be situations in which you want to save the environment way yourself. If you save the environment away by hand, in your own memory location, you must protect it using the same mechanisms. You can either increment and decrement the reference counters, and share the environment, or, if you don't need the environment to be shared, you can copy the environment instead of locking it, which is safer in a multithreaded environment (the environment reference count currently has no mutex associated with it).

void GalSS_EnvLock(GalSS_Environment *env)
Increments the reference counter on the environment env to indicate that it should not be freed.

void GalSS_EnvUnlock(GalSS_Environment *env)
Decrements the reference counter on the environment env. When the reference counter reaches 0, the environment is freed.

GalSS_Environment *GalSS_EnvCopy(GalSS_Environment *old_env)
Copies the environment old_env and sets the reference counter in the new environment to 1.

GalSS_Environment *GalSS_EnvCopyNoLock(GalSS_Environment *old_env)
Copies the environment old_env. The reference counter in the new environment remains at 0. This function is present mostly for internal backward compatibility.


Administrative functions

These are the functions the infrastructure uses to manage call environments. You will seldom need to use these functions directly.

GalSS_Environment *GalSS_EnvCreate(GalIO_CommStruct *gcomm)
Creates an environment from the connection gcomm.

int GalSS_EnvReturnRequired(GalSS_Environment *env)
Returns 1 if the call environment represents a dispatch function invocation for which a return is expected, or 0 if it does not.


License / Documentation home / Help and feedback
Last updated July 29, 2002