In the previous chapter, we learned how to create an ET system, and in this chapter we'll learn to use an existing system. This chapter shows how users can attach to ET systems, define, create and remove stations, attach to and detach from stations, handle events, and handle signals.
Opening a system is done by calling et_open (et_sys_id* id, char *filename,et_openconfig config). The user defines a variable of type et_sys_id and passes its pointer - a value-result argument - which then gives back an "ID" to the open ET system. In addition, the filename of an existing ET system and a parameter describing how the user would like to open the system are passed as parameters to et_open.
There are a number of functions used to create and define the config argument. It is initialized by a call to et_open_config_init (et_openconfig *config). When the user is finished using the configuration, et_open_config_destroy (et_openconfig config) must be called in order to properly release all memory used.
After initialization, calls can be made to functions which set various properties of the specific configuration. Calls to these setting functions will fail unless the configuration is first initialized. The functions used to SET these properties are listed below along with an explanation for each:
More on remote ET systems can be found in the chapter entitled Remote ET. All of the above "set" functions have their counterpart "get" functions as well.
Once an ET system has been opened, users can use the id as a handle for that particular system. Users can open more than one system at a time, referring to each by their respective handles.
Analogous to the opening or creation of ET systems, users begin by declaring a variable of type et_statconfig. Once this variable is declared, it must be initialized before further use. Thus users must also call the function et_station_config_init(et_statconfig* sconfig). After initialization, calls can be made to functions which set various properties of the specific configuration. Calls to these setting functions will fail unless the configuration is first initialized.
When the user is finished using a configuration variable, the user must call et_station_config_destroy(et_statconfig sconfig) with the configuration as an argument in order to properly release all memory used.
The functions used to SET station parameters are listed below along with an explanation for each:
Just a few notes on some of the details. When selecting the ET_STATION_RESTORE_IN mode for event restoration, be aware of a few things. If there is only one process attached to such a station and it dies, the events go to the output list in order to prevent them from being lost to a station with no event readers. If there is more than one process attached and one dies, its events will be put into the input list with the assumption that the recovered events are higher in priority to those already in the station's input list. To be exact, the recovered high priority events are placed "above" (sooner to be read) all other events, and the recovered low priority events are placed below high priority but above all other low priority events. There are no guarantees that the recovered events will be in their original order.
The mode denoted by ET_STATION_SELECT_MATCH has the following behavior. A check is made to see if the first element of the station's selection array is equal to -1. If it is, then the first element of the event's control array is ignored and the event is not marked for selection. Similar comparisons continue for each element of the arrays. Thus, if all elements of a station's selection array are set to -1, the event will NOT be selected. If the first element of the station's selection array is not -1 but is equal to the first element of the event's control array, then the event is selected. If the bitwise AND (&) of the station's and event's second elements is true, then the event is selected. This pattern is repeated with the even elements 0,2,4, 6, ... compared for equality and the odd elements 1, 3, 5, ... compared for bitwise AND. If any of the comparisons are true, then the event is selected. This is the logic employed by the old DD system in its "conditional" mode.
Similar functions to those mentioned above are available to GET the values associated with a station configuration.
Since one of the more difficult tasks facing the first time user is how to properly configure a station, let's look at two examples first:
/* declarations */
et_stat_config sconfig;
/* set values */
et_station_config_init(&sconfig);
et_station_config_setselect(sconfig, ET_STATION_SELECT_ALL);
et_station_config_setblock(sconfig, ET_STATION_NONBLOCKING);
et_station_config_setuser(sconfig, ET_STATION_USER_SINGLE);
et_station_config_setrestore(sconfig, ET_STATION_RESTORE_GC);
et_station_config_setcue(sconfig, 20);
Here is a station to which only 1 user may attach. It accepts all events no matter what values the selection integers have. It is nonblocking, meaning that once the system fills up its input list with a maximum of 20 events, all other events will bypass the station and be placed somewhere downstream. If the user process should die, the events that it owns will be placed back in grandcentral station, and no one else will get them.
A more complicated example can be seen below:
/* declarations */
int selections[] = {17,22,-1,-1};
et_stat_config sconfig;
/* set values */
et_station_config_init(&sconfig);
et_station_config_setselect(sconfig, ET_STATION_SELECT_ALL);
et_station_config_setblock(sconfig, ET_STATION_BLOCKING);
et_station_config_setuser(sconfig, ET_STATION_USER_MULTI);
et_station_config_setrestore(sconfig, ET_STATION_RESTORE_IN);
et_station_config_setprescale(sconfig, 5);
et_station_config_setselect(sconfig, ET_STATION_SELECT_USER);
et_station_config_setselectwords(sconfig, selections);
if (et_station_config_setlib(sconfig, "/stuff/libet_user.so") == ET_ERROR)
{
printf(" cannot set library\n");
}
if (et_station_config_setfunction(sconfig, "et_my_function") == ET_ERROR)
{
printf("cannot set function\n");
}
In the above example, there is a station to which multiple users can attach. Its select mode (ET_STATION_SELECT_USER) says that the user will be supplying a function in a shared library to determine which events are to be selected. Since this station is set to block events, all events which meet its selection criteria are placed in its input list, even if it means slowing the whole ET system down to a crawl. Actually, the prescale factor imposes an additional selection criterion since it is in blocking mode. Thus, only every 5th event which passes through the user's filter gets placed in the station's input list. Its restore mode says that if this user process should ever die, the events that it currently owns will be placed in the station's input list.
Once a configuration is defined, it is passed to the function et_station_create(et_sys_id id, et_stat_id *stat_id, char *stat_name, et_statconfig sconfig). In addition to the arguments, id and sconfig, which have already been covered, the user must supply a unique name and is returned a station identification number stat_id. This station id is used in other station-related routines.
Possible errors returned by the function et_station_create are ET_ERROR_EXISTS if a station by that name exists already, ET_ERROR_TOOMANY if the user is the second user to try to attach to a station designated for one user only, or ET_ERROR for other unrecoverable errors. If the user is a remote consumer, the error ET_ERROR_REMOTE indicates a bad arg or not being able to allocate memory, and ET_ERROR_READ & ET_ERROR_WRITE indicate problems with the network communication.
Removing stations can be accomplished by calling et_station_remove(et_sys_id id, et_stat_id stat_id).
Until a user's process attaches to a station, the station is placed in an idle mode, meaning, that it is not participating in the flow of events - it is getting by-passed. Once a process attaches to a station, it becomes active and begins to receive events. This logic ensures that events do not get stuck in stations with no one to process them or that the entire flow of events does not come to a grinding halt.
Attach to a station by calling et_station_attach(et_sys_id id, et_stat_id stat_id, et_att_id *att). This routine returns a unique attachment number, att, by which a process identifies itself in certain function calls. For example, when reading and writing events, this parameter is required. In this manner, a single process can attach to different stations and yet be differentiated by the ET system. With this type of interface, for example, a user could conceivably have multiple threads with each attached to the same station on a different attachment. The idea is that this id represents a single attachment to a single station. This attachment id is also a way of specifying the ownership of an event - which is important in setting limits on how and where events can flow.
To detach from a station call et_station_detach(et_sys_id id, et_att_id att). If a user is the last one to detach from a station, all of the events left in the station's input list are passed to the output list. In addition, after a user detaches, a search is made for any events that were read but not written back into the ET system by att. They are recovered and placed according to the station's property set by the function et_station_config_setrestore.
Some of the parameters that define a station's behavior as well as its position in the linked list of stations may be modified while an ET system is operating. The only thing that cannot be done is to load new user-defined event selection functions or to change the select mode of the station.
The functions used to SET station parameters are listed below along with an explanation for each:
Similar functions to those mentioned above are available to GET the values associated with a station's configuration. Note that none of the above functions are allowed to modify GRAND_CENTRAL station.
After opening an ET system, creating a station, and attaching to it, users are ready to start creating, reading and writing events.
When creating an new event, users are called producers. There are two routines that can be used for doing this. The first is for getting a single, blank event by calling et_event_new(et_sys_id id, et_att_id att, et_event **pe, int wait, struct timespec *time, int size). At this point users are familiar with the first two arguments id, and att. The third is a pointer to a pointer to an event. In the code, declare a pointer to an event (i.e. et_event *pe) and pass its address. Upon a successful return, pe points to a new event. The fourth arg, wait, is a flag that can be set by using some predefined macros. By setting this wait to ET_SLEEP, the call will block until the next free event is available. By setting it to ET_ASYNC, the call returns immediately with a status. And by setting it to ET_TIMED, the call waits for the amount of time given by the time arg if no events are immediately available. Be warned that the time specified with ET_TIMED mode is a minimum. First, read access to a station's input list must be obtained and that could take some additional time. Finally, the last arg is the requested size in bytes. If the size is larger than those the system was created with, the newly created event will be declared a special "temporary" event and will allocate the necessary memory. (This, of course, slows things down).
Similarly the user can call et_events_new(et_sys_id id, et_att_id att, et_event *pe[], int wait, struct timespec *time, int size, int num, int *nread) for obtaining an array of new events. In this case, pe is an array of pointers to events, num is the number of events desired, and nread is the number of events actually read and placed into the array (which may be less than what was asked for).
When reading events, users are called consumers. There are two routines that can be used for reading. The first is for reading single events and has the form et_event_get(et_sys_id id, et_att_id att, et_event **pe, int wait, struct timespec *time). The arguments are the same as those for creating a new event but without the size.
The second type of routine is for reading an array of events by using the call, et_events_get(et_sys_id id, et_att_id att, et_event **pe, int wait, struct timespec *time, int num, int *nread). The arguments are almost the same as for reading single events with the exception that the user passes an array of pointers to events. There are also additional arguments specifying the number of events the user wants to read and the number actually read. Although less events may be returned, the user will never get more than the amount asked for.
After reading an event, the user has access to a number of its properties for manipulation. Routines to accomplish this are given in the following list:
After setting an event's priority, data length, control array and perhaps its endian value, and writing data, the user is finished with the event and wishes to place it into the ET system. Or perhaps the user has only read the data and is done with the event. In any case, the event must be written back into the system by two possible means. Either write a single event with et_event_put(et_sys_id id, et_att_id att, et_event *pe) or write multiple events with et_events_put(et_sys_id id, et_att_id att, et_event *pe[], int num). In the latter case, the user gives the number, num, of events to put back in the array pe. All events will always be successfully written and will never block as a station's output list has enough room for all events in the whole ET system.
The ET system checks to see if the att that read the event is the same one that is writing it. If it isn't, the call returns an error and nothing is written.
After reading existing events or creating new ones, it's possible that these events may no longer be of interest to the user or any other user on the system. In that case, one may dump or recycle these events by calls to two routines. They are identical to the routines et_event(s)_put in their arguments. The first is et_event_dump(et_sys_id id, et_att_id att, et_event *pe) and dumps a single event. Similarly, et_events_dump(et_sys_id id, et_att_id att, et_event *pe[], int num) dumps multiple events.
When finished using an ET system, it can be removed from a process' memory by using the et_close(et_sys_id id) routine. This unmaps the ET system memory from the process and makes it inaccessible. It also stops the heartbeat and system-heartbeat-monitor threads. In order to close, all attachments must be detached first. However, there is another function et_forcedclose(et_sys_id id) which will automatically do all the detaching first. Of course, the ET system continues to function for other processes as before.