$Header$ Glish changes ============= 10Mar95: - 2.5.0 release. You must recompile and relink your present Glish clients. To build Glish 2.5, you need flex 2.4.6 or higher (current is 2.4.7), which you can get from ftp.ee.lbl.gov. The Glish installation has changed considerably - see README. - Glish now runs under SunOS, IRIX, HP-UX, Solaris, and AIX. - Included in the Glish distribution is a "contrib/" directory containing contributed (but not supported) Glish clients. Presently, contrib/ includes a Glish <-> Tcl/TK interface, a Glish <-> Perl 5.0 interface, and a (fledgling) Glish <-> EPICS interface. - Four new types have been added: byte (unsigned integer in the range 0 .. 255), short, complex, and dcomplex (double-precision complex). The first two were contributed by Chris Saltmarsh, the last two by Darrell Schiebel. To create a byte or short value, use as_byte(x) or as_short(x) for some integer x. dcomplex constants are specified using the same notation as in S: 5 + 3i 6-2.93i 3.14i all specify double-precision complex constants. To create a single-precision complex value, use as_complex(x). The function complex(r,i) returns a complex/dcomplex value whose real parts are taken from the vector 'r' and imaginary parts from 'i'. The four new types are all "numeric" and support the usual numeric operations (with the complex operators patterned after S). Type promotion is now ordered as: bool, byte, short, integer, float, double, complex, dcomplex, except that mixing double with complex promotes to dcomplex. The real part of an imaginary value is retrieved using the real(z) function, and the imaginary part using imag(z). - A general framework of "attributes" associated with a value has been contributed by Darrell Schiebel. A value's attributes are accessed using the "::" operator. For example, a::foo := 1:3 assigns [1, 2, 3] to a's "foo" attribute (regardless of a's type). Then executing a::bar := a::foo * 2 would assign [2, 4, 6] to a's "bar" attribute. A value's attributes are themselves a record value (whose elements might also have attributes). To assign all of a value's attributes: a:: := [foo=1:3, bar=(1:3)*2] and to access the record directly, just use the expression "a::". When values with attributes are operated on, the attributes of the operand (if a unary operator) or the longer operand (binary operator) are propagated to the result. - Using these attributes, Darrell has also added multi-dimensional arrays (Darrell's been busy!). In Glish, all values are intrinsically one-dimensional arrays (which are now referred to as "vectors"). This underlying vector however can be intrepreted as a multi-dimensional array by associating a "shape" attribute with the value. Thus, a := 1:9 a::shape := [3,3] associates with a's vector a shape of a 3x3 array. Printing 'a' at this point yields: [[1:3,] 1 4 7 2 5 8 3 6 9] The first row reports the portion of the array being printed. Arrays are created using the array() function, which takes as its first argument a value whose vector is used for the initial array elements, and whose remaining values give the dimensions of the array. For example, we could have created 'a' using: a := array(1:9,3,3) As discussed above concerning propagation of attributes, operating on arrays preserves their dimensionality. Arrays of different shape but the same number of elements can be mixed in operations; the result has the shape of the lefthand operand. Arrays can be indexed in several ways. As before, a boolean index serves as a mask for selecting array elements. Arrays can also be indexed using multiple subscripts: a[2,1] yields 2, a[1:2,2:3] yields [[1:2,] 4 7 5 8] while a[2:3,] yields [[1:2,] 2 5 8 3 6 9] and a[,1:2] yields [[1:3,] 1 4 2 5 3 6] Arrays can also be indexed using another array with as many columns as 'a' has dimensions. This operation is referred to as a "pick". Each row in the index array specifies a single element from 'a'. For example, given d := array([1,2,2,3],2,2) then a[d] selects a[1,2] and a[2,3], yielding [4, 8]. - Glish now supports "subreferences" to portions of arrays. For example, a := [1, 3, 5, 7, 9, 11, 13] b := ref a[[2, 6]] associates 'b' with the 2nd and 6th element of 'a'. This association lasts until either 'a' or 'b' is assigned to. Assigning to elements of 'a' or 'b', though, preserves the connection. For example, b[2] := 14 changes the value of 'a' to [1, 3, 5, 7, 9, 14, 13]. Subreferences also work with multi-dimensional arrays. Subreferences were contributed by Darrell Schiebel. - Glish is now configured using an autoconf-generated "configure" script. This means to build Glish, you go to the top level directory and type "./configure". The script then generates Makefile (from Makefile.in) and config.h (from config.h.in) files, which contain all of the system dependencies for your environment. The SDS library has not yet been converted to autoconf, nor has the new editline library (see below), though they use it for minor configuration issues such as which C compiler to use. - When run interactively, Glish now uses the "editline" library written by Simmule Turner and Rich Salz, and integrated into Glish by Darrell Schiebel. Editline lets you do emacs-style line editing on the text you type to the Glish interpreter, similar to "tcsh". If for some reason you don't want to use editline (or it doesn't built for your system), you can turn off its use by undefining USE_EDITLINE in glish/input.h. - The new prod(...) function returns the scalar product of its arguments, analogous to the existing sum(...) function. (DS) - The new missing() function returns a boolean vector which is T if the corresponding argument (numbered from left to right) was missing in the present call to the function (and thus took on its default value), or F if the argument was explicitly passed. (DS) - In conjunction with this missing() function, you can now omit positional arguments in function calls. E.g., foo(1,,,5). - The length() function now returns a vector of the lengths of its arguments. So length(1:5, 2:20) returns [5, 19]. (DS) - rep() can now be called with non-scalar arguments. (DS) - min(), max(), and range() are now correctly documented to operate on an arbitrary number of arguments. - The sync(c) function can be called to synchronize the Glish script's execution with that of client 'c'. The call does not return until client 'c' has processed all events/requests previously sent to it. - The sort(x) function returns the vector x sorted into ascending order. x's type must be either numeric or string. (This function is written entirely in Glish, by the way.) - sort_pair(x,y) rearranges the elements of y to reflect the order of the elements of x (x and y must have the same length). For example, sort_pair([3,1,2], "a b c") returns "b c a", since "b" was paired with the smallest element of [3,1,2], "c" with the second smallest, and "a" with the largest. - order(x) returns the indices of the sorted elements of x. So, for example, x[order(x)] == sort(x). - The precedence of the val/ref/const operators has been changed. It used to be very low, and now is very high (equivalent to the '!' operator). This fixes a bug in grouping val a := 5 as val (a := 5) - The "glish.init" startup Glish script is now compiled into the Glish interpreter, and no longer needs to be located at run-time. Related to this, the $glish_init environment variable is no longer used to locate an alternative glish.init file at run-time. - The SDS library has been heavily modified, courtesy of Chris Saltmarsh and Todd Satogata. The main changes are improved buffering and bug fixes. - A new member function, Client::Error(const char* msg), is available for Glish clients to post an "error" event. There is also a version that formats its message using a single string argument, Client::Error(const char* fmt, const char* arg). Eventually these routines will correctly indicate errors for request/reply events, but at present they don't. - Client::AddInputMask now returns the number of new input fd's it added to the mask. - Client::HasSequencerConnection() is deprecated; use Client::HasInterpreterConnection() instead. - A new member function Client::PostEvent(const char* event_name, const char* event_fmt, const char* arg1, conts char* arg2), posts an event with the given name and a printf-format taking two string arguments. - A new member function Client::ReplyPending() returns true if the client has a request/reply pending and false otherwise. - You can now call Client::PostEvent with a nil Value pointer for the event's value, if you know the value will be ignored. - The GlishEvent class has been spruced up a bit, and now supports a member function IsRequest() which returns true if the event was a request and false otherwise. - If a Glish client is fired up standalone (from a shell instead of via Glish), by default it now considers itself to not have any event source. Calls to NextEvent() will return nil, and calls to PostEvent() will simply discard the posted events. To force the client to use stdin as its event source and stdout as its event sink, run it with "-glish" as the first argument. - The "bool" C++ type has been changed to "glish_bool" to avoid conflicts with ANSI C++, which has a builtin "bool" type. A number of Value and Client member functions that used to have bool operands now have int operands instead. - The abbreviations of "true" and "false" for "glish_true" and "glish_false" have been removed, due to conflict with ANSI C++. - Value::FieldVal( const char field[], charptr& val ) is now correctly typed as FieldVal( const char field[], char*& val ), reflecting the fact that the caller should delete val when done with it. - The C language Glish client interface is no longer supported. - Glish now builds with flex 2.4.7. - A bug was fixed in using boolean values in arithmetic operations. - A bug was fixed in recognizing "established" events from clients running on the same host as the Glish interpreter. - Some minor memory leaks have been fixed. 23Aug93: - 2.4.1 release. If you relink a Glish client to the libglish.a of this release, then you *must* also recompile any of the client's sources that create Client objects (because they've changed in size). - A new overview paper, "Glish: A Software Bus for High-Level Control", is available in doc/ICALEPCS-93.ps. It is slanted towards using Glish for accelerator control (rather than as a general software bus). - Glish now has a mechanism for synchronous request/reply events: result := request a->b( 1:10 ) sends a "b" event to "a" with value 1:10 and then waits for "a" to reply. The value of a's reply is stored in "result". Note that "request" is a new keyword, which may cause incompatibilities (syntax errors) with existing scripts that have variables with that name. "a" must be a Glish client (not a user agent such as a subsequence). The client responds to a request using the Client::Reply member function (it is up to the client to know which of the different events it receives correspond to request/reply). Glish generates a warning if the client first generates any other event (i.e., by using Client::PostEvent) or if Client::Reply is used when a request is not pending. Presently, when a request/reply is active no other events are read by the Glish interpreter until the reply is received; perhaps this will change in the future, as we understand request/ reply usage better. Another possible future change is the addition of a timeout. See the Glish User Manual for details. - The "event-send" statement now takes an optional "send" keyword. That is, you can write foo->bar( args ) instead as send foo->bar( args ) The belief is that using "send" will lead to more readable scripts, and the plan is to gradually phase in "send" as a mandatory keyword. - A new member function int Client::HasEventSource() returns true if a Glish client has *any* input source (either a connection to the Glish interpreter, or by reading from stdin), and false if it has no input source (due to using -noglish). - The "[]" expression now creates a truly empty array; previously it created an array with a single element, the boolean constant F. Such empty arrays can be used when constructing other arrays, even if the other array elements have incompatible types. For example, ["foo", "bar", []] constructs a two-element string array, but ["foo", "bar", [T]] results in an error because the types "string" and "boolean" are incompatible. - A bug introduced in the last release in which the completion of an "await" statement could lead to $value not being set has been fixed. - A backward-compatibility bug in reading Glish datasets/SDS's written with old versions of Glish has been fixed. 26Jul93: - 2.3.2 release. - Fixed slow memory leak in sending events. - A number of minor tweaks to keep cfront-derived C++ compilers happy. 21Jul93: - 2.3.1 release. - New "activate" and "deactivate" statements can be used to control whether the body of a "whenever" statement is active (i.e., whether it's executed when its corresponding event comes in). - Four new built-in functions support the activate/deactivate statements: whenever_stmts(agent) returns a record listing the event names and "whenever" statements indices corresponding to the given agent. active_agents() returns a record array listing all of the currently active agents. current_whenever() returns the index of the currently-executing (or most recently-executed) "whenever" statement. Here "executing" refers to executing the *body* of the "whenever" statement. last_whenever_executed() returns the index of the last executed whenever statement, where "executed" refers to the statement as a whole and not to its body. - Each host now runs at most one copy of the Glish daemon glishd. glishd listens on TCP port 9991 for connections from Glish interpreters. When it receives one it opens a socket connection which the interpreter uses to communicate with the daemon by sending it events, much as before. Each daemon can support multiple interpreters at the same time. If an interpreter finds that no daemon is presently running on a remote host then it creates one using rsh as before. The daemon does not, however, exit when the interpreter does, but instead persists. This means that you may find encounter a daemon owned by someone else running on a machine you want to use. For the present the assumption is that this is not a major problem; but conceivably you might need the daemon to be running with your uid and not some other persons. For now what you do is tell the daemon to exit using the "tell_glishd" program (see below). In the future we may have to make the daemon setuid-root so it can change its uid as needed. - The Glish interpreter probes each remote daemon every five seconds by sending it a "probe" event. If the daemon does not reply to this event within the next five seconds, the interpreter deems network connectivity lost, issues a warning, and generates a "connection_lost" event for the global "system" agent (see next item). If the daemon subsequently replies to a later probe, the interpreter reports this fact and generates a "connection_restored" event. If the daemon dies or is killed using tell_glishd, the interpreter generates a "daemon_terminated" event. - A new agent record, "system", manages information about the general environment in which a script runs. "system" presently has two information fields: system.version The version number of the Glish interpreter. system.is_script_client True if this script is a client of another script, false otherwise. "system" also generates the following events: system->connection_lost Indicates that connectivity to a remote host has been lost. The event value names the host. system->connection_restored Indicates that connectivity to a remote host has been recovered. The event value names the host. system->daemon_terminated Indicates that Glish daemon on a remote host has died. Again, the event vlue names the host. You can set up "whenever"'s for these events just like for those generated by any other agent. If you find you'd like other "system" events, let me know; in general they're not hard to add. - The "version" global has been removed, since it's now subsumed by "system.version". - Glish now supports an "include" directive for including the contents of a source file: include "foo.g" includes the contents of the file "foo.g". There's no limit on the nesting-depth. - When running Glish interactively, you can now create clients and execute "whenever" statements. You also can execute scripts by "include"'ing them. - You can now use the "==" and "!=" operators to compare non-numeric, non-string values. Two such values compare as equal if they refer to exactly the same entity. For example, a := [b=1, c=2] d := [b=1, c=2] e := ref a print a == a, a == d, a == e prints T, F, T. - Glish now allows only one filename on the command line (since the "include" directive can be used to access multiple sources). Because of this change, you no longer need the special "--" argument to delimit the end of source filenames and the beginning of script arguments. Instead, every argument save the first is treated as a script argument. "--" is still allowed, though, for backward compatibility. - A new program (not a Glish client), tell_glishd, is available for controlling the Glish daemon on a given host. Presently all you can do with tell_glishd is kill the daemon running on a given host. You do so using: tell_glishd -k [host] As indicated, "host" is optional; it defaults to the local host. 03Jun93: - 2.2.2 release. - Fixed deadlock problems when using multiple point-to-point links. - Local point-to-point links no longer use named pipes, but Unix-domain sockets instead. - Fixed bug in which a broken link was treated by the sink side as a "terminate" event. - Added file version.h for tracking version number. 20May93: - 2.2.1 release; Client::FD_Change virtual function was completely broken. 19May93: - 2.2 release. - Assignment (":=") is now an expression instead of a statement, so you can string multiple assignments together like in C: a := b := 1 - Compound assignments such as "x +:= 1" are now supported. - "local" statements can include initializations: local x := 5 - You can use a Glish script as a client in another Glish script: c := client("glish myscript.g") There's a new, associated global variable called "script", which is either boolean "F" if the script is running independently, or an agent that can be used to send and receive events from the script if being run as a script client. - An "opaque" type is available for client data (SDS's) that are uninterpreted by Glish (it just provides a mechanism for conveying them between clients). - The division operator now always converts its operands to Glish "double" values and yields a double value. - The Client class has a new virtual member function, Client::FD_Change, that you can redefine when deriving from Client to receive notification of when a Client's input sources change. - The manual includes a new chapter, "Changes Between Glish Releases", summarizing this information and providing pointers to fuller descriptions in the Glish user manual. - Some portability tweaks for HP/UX. 21Jan93: - 2.1 release. - User documentation is now available in doc/User-Doc.ps. - An "ind(x)" function has been added which returns the indicies corresponding to an array value. For example, ind("hello, how are you?") returns [1 2 3 4], since the argument has four elements. 13Jan93: - Records can now be indexed in the same manner as arrays, i.e., with integer or boolean arrays. For example, r := [a=1, b=2] r[1] := 5 r[2] := "hi" print r prints "[a=5, b=hi]". You also can add fields to a record: r := [=] # create an empty array r[1] := 5 r[2] := "hi" print r prints "[*1=5, *2=hi]", where "*1" and "*2" are internal names for the new record fields just created. This style of record access allows you to create what are effectively arrays of records, functions, or agents, without the restriction (and type-checking) that every element of the array has the same type. - Added BSD-style copyright notice to sources. 01Dec92: - PostScript for a 15-page paper describing Glish is now available in glish/doc/USENIX-93.ps. The paper will appear in the proceedings of the 1993 Winter USENIX conference. - Glish now supports "link" and "unlink" directives for creating and suspending point-to-point connections between Glish clients. Events sent point-to-point are *not* seen by the sequencer; they do not trigger "whenever"'s. The "link" syntax looks like: link a->b to c->d This causes all of a's "b" events to go directly to c, which will see them as "d" events. You can also use: link a->b to c->* which passes the event along without changing its name (i.e., it's still called "b"). The "unlink" directive looks the same: unlink a->b to c->d suspends the point-to-point connection, so a's "b" events will now be seen by the sequencer and trigger any corresponding "whenever"'s. Doing another "link" reactivates the link. - The Client library has undergone several user-visible changes: An "void Unrecognized()" member function has been added; it *must* be called by a client that does not recognize a particular event. "int ReadFD()" has been replaced by routines oriented towards use of select(). "void AddInputMask( fd_set* mask )" is passed an fd_set mask used by select() and adds to it those bits corresponding to the Client's input sources. You then can use "int HasClientInput( fd_set* mask )" to determine whether a mask returned by select() indicates that the client has pending input (i.e., one or more events has arrived). The function returns true or false. A new version of NextEvent() is available; it is passed an fd_set* mask which it uses to determine from where to read the next event. "int WriteFD()" has been removed. - Communication between the sequencer and processes running on the same host is now done using pipes, a substantial performance win. - When creating clients or shell tasks an optional input= argument can be used to set up the program's standard input. For example, shell("wc", input=1:10) will run the "wc" command on the numbers from 1 to 10, one on each line. - shell clients now write their output to a pty if available, so it is line-buffered. - The default "glish.init" file now resides in $(ISTKPLACE)/lib/ instead of $(ISTKPLACE)/glish/. - The "loop" keyword has been replaced with "next" ("continue" can also be used, for die-hard C hackers). - The glish interpreter now prints non-string array values with []'s around them. - The predefined family of "relay" functions now treat a "destination event name" of '*' to mean "use the same name as that the source generated". 01Oct92: - A number of new predefined functions are available: is_function(), is_agent(), and is_numeric() return true if their argument is a function, and agent, or a numeric type, respectively. abs() returns the absolute value of its numeric argument. len() is an alias for length(). rep() takes two arguments, a "value" and a "count", and returns an array of "count" copies of "value". For example, rep(5,3) is [5, 5, 5]. - Clients can be suspended by including "suspend=" on the Glish command line. For example, "glish suspend=myprog foo.g" will run Glish on "foo.g" and whenever a client whose executable is called "myprog" is begun, it will act as though "suspend=T" had been specified in the client() call. - When clients are suspended they now announce themselves along with their host and PID. - The "await" statement has been modified to by default process all other events until the await'd for event(s) arrives. A special version of await, using the keyword "only", will not process the events unless they match the "except" list. - The Value class's constructors for creating Value's from arrays now take an enumerated type as an optional third argument indicating what degree of control the Value is to assume over the array. A value of TAKE_OVER_ARRAY (default) indicates that the array is now the Value's to manage as it pleases (perhaps dynamically growing it, and definitely deleting it when done with it); a value of COPY_ARRAY indicates that the Value should first make a copy of the array; and a value of PRESERVE_ARRAY indicates that the Value should use the array but not dynamically grow it or delete it. COPY_ARRAY is often appropriate for arrays created on the stack in routines that will exit, and PRESERVE_ARRAY is good for static or global arrays, or arrays that are shared among a number of Value's. The third argument used to be a boolean, with false corresponding to TAKE_OVER_ARRAY and true to COPY_ARRAY. - Any line can now be forcibly continued to the next line by escaping its newline. For example, a := b +c is normally interpreted as two statements because a semi-colon is inserted after "b", but a := b \ + c is interpreted as one statement. - The output of shell() commands no longer has a newline at the end of each line; they are stripped off. - The Glish "fatal" global has been #define'd to actually be named "glish_fatal" to avoid somewhat frequent name clashes when linking Glish clients to other libraries. - A bug has been fixed with accessing $value during an "await". - The List class has been modified so that many of BaseList's member functions are now protected. Before this change it was possible to use the wrong type when inserting into a list and have the compiler fail to catch the problem. - The Dictionary class is now implemented using dynamically extended hash tables. Dictionary's can optionally be specified as "ordered", in which case the elements can be accessed in array-fashion, with index 0 corresponding to the first element inserted into the dictionary, index 1 the second, and so on. If an element is deleted from a dictionary then all the indicies are changed to reflect the deletion; for example, if the first element inserted is deleted, index 0 will then correspond to what originally was the second inserted element. 10Aug92: - Changed the paste() predefined function to take an optional argument "sep" which designates the string to be used to glue together the arguments to the call. For example, paste( 3, 5, 7, sep="foo" ) results in "3foo5foo7", and paste( "Test #", 2 ) results in "Test #2". By default sep is a single blank (' '). Note that to preserve leading or trailing whitespace in the separator it must be enclosed in single quotes instead of double quotes. - Fixed bugs in automatic growing of values when elements are assigned beyond the current end of the value's array. - Fixed a bug in constructing string arrays. - Fixed a bug with boolean array indicies. 05Aug92: - Added "FieldVal" member functions to Value class for quick access to the scalar values of record fields. 28Jul92: - Fixed some minor bugs related to "glishmon", the Glish event monitor. - Fixed some bugs related to read_value()/write_value(). 24Jul92: - A new set of Value member functions called SetField() have been added for easily adding fields to records from raw C types (such as an array of float's). 22Jul92: - A -noglish flag has been added to the Client library. When run standalone clients by default read stdin to get new events and write generated events to stdout. -noglish suppresses the reading of events from stdin (but not the writing to stdout; perhaps it should do this too); it's main use is to enable running standalone clients in the background. - Numerous new methods are now available for Value objects, primarily to make it easy to use Value's in client applications. See Glish/Value.h for the latest full list. The main noteworthy changes are routines for accessing record fields. Also, in general Value member functions are now a bit more lenient regarding bad calls (for example, requesting a field from a Value that's not a record), typically returning nil pointers instead of exiting with a fatal error. - A new create_record() function is available to clients for creating Values of type "record". - A bug was fixed in the socket code that could substantially slow down sending events. In particular, interactive applications should now be much snappier. - The "glish.init" file, which must be present in order to run glish, is now found first by checking for the "glish_init" environment variable. If the variable is not set then a hardwired path is used, much as in the past. - A couple of bugs were fixed regarding converting values to strings and from boolean to integer. These previously caused core dumps in some cases. 05Jun92: - The BoolVal()/IntVal()/DoubleVal() member functions of the Value class now take an optional argument specifying which element of the Value to convert and return. - Two new predefined functions: all(x) returns true if when interpreted as a boolean value, all the elements of "x" are true; any(x) returns true if at least one of the elements of "x" is true. - A "terminate" event may now be sent to a client to tell it to terminate. The event looks the same to the client as a broken sequencer connection (i.e., NextEvent() returns 0). 04Jun92: - A "C" client library has been written. See glish_client.h or doc/glish-diffs for a description of its interface. - A bug has been fixed which would cause remotely executed clients to generate sporadic error messages. - A bug in the truncating of long event names has been fixed. 15May92: - Glish has undergone major revisions. See doc/glish-diffs for a summary of differences between the old version of Glish and the new (which is Glish version 2.0). 12Mar92: - The C++ "Client" class has been rewritten to be layered on top of the C client/event_lib libraries. The major benefit of this rewrite is that SDS-valued Glish events are now accessible to C++ Glish clients. See "Client.h" in glish/conn for a description of the relevant member functions. - The C++ "Channel" class has been rewritten to be layered on top of the C ipc_lib library. This change should be transparent, except that the ReadStatus() member function has been removed. - The C event_lib interface has changed in several ways: - event_init() now takes argv passed by *value* rather than by reference. I.e., a typical call to event_init() looks like event_init( &argc, argv ); (there used to be a '&' before argv, too). argv is now modified *in place*. - event_terminate() is a new routine that must be called for the client to gracefully exit. - all routines which take pointer arguments have been modified to declare those arguments "const" if the pointer's contents are not modified. - The C client_lib interface has two new routines: client_channel() returns the ipc_lib channel used for the connection (ipc_lib routines can then be used to read or write from the channel, or to get the corresponding read/write fd's for use in select() operations); client_event_available() returns true if another event is available for processing. client_event_available() does *not* do a select(), so events that have arrived in the local system buffer but that have not yet been read by the ipc_lib routines do *not* cause client_event_available() to return true. These events must be detected using select() calls. client_event_available() is used to make sure all incoming events have been exhausted after a select() has indicated that there are such events, and client_next_event() has been used to read the first such event. The typical code fragment looks like: