diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2017-10-26 16:45:12 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2017-10-26 16:45:12 (GMT) |
commit | 6054f36bb658916bd231b9002efb94444e2871c8 (patch) | |
tree | a815500ec9bf0e100f4075a825213605c382def0 /doc/server.html | |
download | blt-6054f36bb658916bd231b9002efb94444e2871c8.zip blt-6054f36bb658916bd231b9002efb94444e2871c8.tar.gz blt-6054f36bb658916bd231b9002efb94444e2871c8.tar.bz2 |
Squashed 'xpa/' content from commit d6a54301
git-subtree-dir: xpa
git-subtree-split: d6a543018b46c5a6c47b5ff91e0e131549595ca6
Diffstat (limited to 'doc/server.html')
-rw-r--r-- | doc/server.html | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/doc/server.html b/doc/server.html new file mode 100644 index 0000000..2e10b11 --- /dev/null +++ b/doc/server.html @@ -0,0 +1,833 @@ +<!-- =defdoc xpaserver xpaserver 3 --> +<HTML> +<HEAD> +<TITLE>XPA Server API</TITLE> +</HEAD> +<BODY> + +<!-- =section xpaserver NAME --> +<H2><A NAME="xpaserver">XPAServer: The XPA Server-side Programming Interface</A></H2> + +<!-- =section xpaserver SYNOPSIS --> +<H2>Summary</H2> +A description of the XPA server-side programming interface. + +<!-- =section xpaserver DESCRIPTION --> +<H2><A NAME="intro">Introduction to XPA Server Programming</H2></A> +<P> +Creating an XPA server is easy: you generally only need to call the +XPANew() subroutine to define a named XPA access point and set up the +send and receive callback routines. You then enter an event loop such +as XPAMainLoop() to field XPA requests. +<PRE> + #include <xpa.h> + + XPA <A HREF="./server.html#xpanew">XPANew</A>(char *class, char *name, char *help, + int (*send_callback)(), void *send_data, char *send_mode, + int (*rec_callback)(), void *rec_data, char *rec_mode); + + XPA <A HREF="./server.html#xpacmdnew">XPACmdNew</A>(char *class, char *name); + + XPACmd <A HREF="./server.html#xpacmdadd">XPACmdAdd</A>(XPA xpa, + char *name, char *help, + int (*send_callback)(), void *send_data, char *send_mode, + int (*rec_callback)(), void *rec_data, char *rec_mode); + + void <A HREF="./server.html#xpacmddel">XPACmdDel</A>(XPA xpa, XPACmd cmd); + + XPA <A HREF="./server.html#xpainfonew">XPAInfoNew</A>(char *class, char *name, + int (*info_callback)(), void *info_data, char *info_mode); + + int <A HREF="./server.html#xpafree">XPAFree</A>(XPA xpa); + + void <A HREF="./server.html#xpamainloop">XPAMainLoop</A>(void); + + int <A HREF="./server.html#xpapoll">XPAPoll</A>(int msec, int maxreq); + + void <A HREF="./server.html#xpaatexit">XPAAtExit</A>(void); + + void <A HREF="./server.html#xpacleanup">XPACleanup</A>(void); + +</PRE> + +<H2>Introduction</H2> + +To use the XPA application programming interface, a software developer +generally will include the xpa.h definitions file: +<PRE> + #include <xpa.h> +</PRE> +in the software module that defines or accesses an XPA access point, and +then will link against the libxpa.a library: +<PRE> + gcc -o foo foo.c libxpa.a +</PRE> +XPA has been compiled using both C and C++ compilers. + +<P> +A server program generally defines an XPA access point by calling the +XPANew() routine and specifies "send" and/or "receive" callback +procedures to be executed by the program when an external process +either sends data or commands to this access point or requests data or +information from this access point. A program also can define several +sub-commands for a single access point by calling XPACmdNew() and +XPACmdAdd() instead. Having defined one or more public access points +in this way, an XPA server program enters its usual event loop (or +uses the standard XPA event loop). + +<!-- =defdoc xpanew xpanew 3 --> + +<!-- =section xpanew NAME --> +<H2><A NAME="xpanew">XPANew: create a new XPA access point</A></H2> + +<!-- =section xpanew SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + XPA XPANew(char *class, char *name, char *help, + int (*send_callback)(), + void *send_data, char *send_mode, + int (*rec_callback)(), + void *rec_data, char *rec_mode); +</PRE> +</B> + +<!-- =section xpanew DESCRIPTION --> +<P> +Create a new XPA public access point with the class:name +identifier <A HREF="./template.html">template</A> +and enter this access point into the XPA name server, so that it +can be accessed by external processes. XPANew() returns an XPA struct. +Note that the length of the class and name designations must be less +than or equal to 1024 characters each. + +<P> +The XPA name server daemon, xpans, will be started automatically if it +is not running already (assuming it can be found in the path). The +program's ip address and listening port are specified by the +environment variable XPA_NSINET, which takes the form <ip>:<port>. If +no such environment variable exists, then xpans is started on the +current machine listening on port 14285. It also uses 14286 as a +known port for its public access point (so that routines do not have +to go to the name server to find the name server ip and port!) +As of XPA 2.1.1, version information is exchanged between the xpans +process and the new access point. If the access point uses an XPA +major/minor version newer than xpans, a warning is issued by both processes, +since mixing of new servers and old xpa programs (xpaset, xpaget, +xpans, etc.) is not likely to work. You can turn off the warning +message by setting the XPA_VERSIONCHECK environment variable to "false". + +<P> +The help string is meant to be returned by a request from xpaget: +<PRE> + xpaget class:name -help +</PRE> +<P> +A send_callback and/or a receive_callback can be specified; at +least one of them must be specified. + +<P> +A send_callback can be specified that will be executed in response to +an external request from the xpaget program, the XPAGet() routine, or +XPAGetFd() routine. This callback is used to send data to the +requesting client. + +<P> +The calling sequence for send_callback() is: +<PRE> + int send_callback(void *send_data, void *call_data, + char *paramlist, char **buf, size_t *len) + { + XPA xpa = (XPA)call_data; + ... + return(stat); + } +</PRE> +<P> +The send_mode string is of the form: "key1=value1,key2=value2,..." +The following keywords are recognized: +<PRE> + key value default explanation + ------ -------- -------- ----------- + acl true/false true enable access control + freebuf true/false true free buf after callback completes +</PRE> +<P> +The call_data should be recast to the XPA struct as shown. In +addition, client-specific data can be passed to the callback in +send_data. + +<P> +The paramlist will be supplied by the client as qualifying parameters +for the callback. There are two ways in which the send_callback() +routine can send data back to the client: + +<P> +1. The send_callback() routine can fill in a buffer and pass back a +pointer to this buffer. An integer len also is returned to specify the +number of bytes of data in buf. XPA will send this buffer to the +client after the callback is complete. + +<P> +2. The send_callback can send data directly to the client by writing +to the fd pointed by the macro: +<PRE> + xpa_datafd(xpa) +</PRE> +<P> +Note that this fd is of the kind returned by socket() or open(). + +<P> +If a buf has been allocated by a standard malloc routine, filled, and +returned to XPA, then freebuf generally is set so that the buffer will +be freed automatically when the callback is completed and data has +been sent to the client. If a static buf is returned, freebuf should +be set to false to avoid a system error when freeing static storage. +Note that default value for freebuf implies that the callback will +allocate a buffer rather than use static storage. + +<P> +On the other hand, if buf is dynamically allocated using a method +other than a standard malloc/calloc/realloc routine (e.g. using Perl's +memory allocation and garbage collection scheme), then it is necessary +to tell XPA how to free the allocated buffer. To do this, use the +XPASetFree() routine within your callback: +<PRE> + void XPASetFree(XPA xpa, void (*myfree)(void *), void *myfree_ptr); +</PRE> +The first argument is the usual XPA handle. The second argument is the +special routine to call to free your allocated memory. The third +argument is an optional pointer. If not NULL, the specified free +routine is called with that pointer as its sole argument. If NULL, the +free routine is called with the standard buf pointer as its sole +argument. This is useful in cases where there is a mapping between the +buffer pointer and the actual allocated memory location, and the +special routine is expecting to be passed the former. + +<P> +If, while the callback performs its processing, an error occurs that +should be communicated to the client, then the routine XPAError should be +called: +<PRE> + XPAError(XPA xpa, char *s); +</PRE> +<P> +where s is an arbitrary error message. The returned error message +string will be of the form: +<PRE> + XPA$ERROR [error] (class:name ip:port) +</PRE> +<P> +If the callback wants to send a specific acknowledgment message back +to the client, the routine XPAMessage can be called: +<PRE> + XPAMessage(XPA xpa, char *s); +</PRE> +<P> +where s is an arbitrary error message. The returned error message +string will be of the form: +<PRE> + XPA$MESSAGE [message] (class:name ip:port) +</PRE> +<P> +Otherwise, a standard acknowledgment is sent back to the client +after the callback is completed. + +<P> +The callback routine should return 0 if no error occurs, or -1 to +signal an error. + +<P> +A receive_callback can be specified that will be executed in response +to an external request from the xpaset program, or the XPASet (or +XPASetFd()) routine. This callback is used to process data received +from an external process. + +<P> +The calling sequence for receive_callback is: +<PRE> + int receive_callback(void *receive_data, void *call_data, + char *paramlist, char *buf, size_t len) + { + XPA xpa = (XPA)call_data; + ... + return(stat); + } +</PRE> +<P> +The mode string is of the form: "key1=value1,key2=value2,..." +The following keywords are recognized: +<PRE> + key value default explanation + ------ -------- -------- ----------- + acl true/false true enable access control + buf true/false true server expects data bytes from client + fillbuf true/false true read data into buf before executing callback + freebuf true/false true free buf after callback completes +</PRE> +<P> +The call_data should be recast to the XPA struct as shown. In +addition, client-specific data can be passed to the callback in +receive_data. + +<P> +The paramlist will be supplied by the client. In addition, if the +receive_mode keywords buf and fillbuf are true, then on entry into the +receive_callback() routine, buf will contain the data sent by the +client. If buf is true but fillbuf is false, it becomes the callback's +responsibility to retrieve the data from the client, using the data fd +pointed to by the macro xpa_datafd(xpa). If freebuf is true, then buf +will be freed when the callback is complete. + +<P> +If, while the callback is performing its processing, an error occurs +that should be communicated to the client, then the routine XPAError +can be called: +<PRE> + XPAError(XPA xpa, char *s); +</PRE> +<P> +where s is an arbitrary error message. + +<P> +The callback routine should return 0 if no error occurs, or -1 to +signal an error. + +<!-- =defdoc xpacmdnew xpacmdnew 3 --> + +<!-- =section xpacmdnew NAME --> +<H2><A NAME="xpacmdnew">XPACmdNew: create a new XPA public access point for commands</A></H2> + +<!-- =section xpacmdnew SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + XPA XPACmdNew(char *class, char *name); +</PRE> +</B> + +<!-- =section xpacmdnew DESCRIPTION --> +<P> +Create a new XPA public access point for commands that will share a +common identifier class:name. Enter this access point into the XPA +name server, so that it can be accessed by external processes. +XPACmdNew() returns an XPA struct. + +<P> +It often is more convenient to have one public access point that can +manage a number of commands, rather than having individual access +points for each command. For example, it is easier to command the +ds9 image display using: +<PRE> + echo "colormap I8" | xpaset ds9 + echo "scale log" | xpaset ds9 + echo "file foo.fits" | xpaset ds9 +</PRE> +<P> +then to use: +<PRE> + echo "I8" | xpaset ds9_colormap + echo "log" | xpaset ds9_scale + echo "foo.fits" | xpaset ds9_file +</PRE> +<P> +In the first case, the commands remain the same regardless of the +target XPA name. In the second case, the command names must change +for each instance of ds9. That is, if a second instance of ds9 +called DS9 were running, it would be commanded either as: +<PRE> + echo "colormap I8" | xpaset DS9 + echo "scale log" | xpaset DS9 + echo "file foo.fits" | xpaset DS9 +</PRE> +<P> +or as: +<PRE> + echo "I8" | xpaset DS9_colormap + echo "log" | xpaset DS9_scale + echo "foo.fits" | xpaset DS9_file +</PRE> +<P> +Thus, in cases where a program is going to manage many commands, it +generally is easier to define them as commands associated with the +XPACmdNew() routine, rather than as separate access points using +XPANew(). + +<P> +When XPACmdNew() is called, only the class:name identifier is +specified. Each sub-command is subsequently defined using the +XPACmdAdd() routine. + +<!-- =defdoc xpacmdadd xpacmdadd 3 --> + +<!-- =section xpacmdadd NAME --> +<H2><A NAME="xpacmdadd">XPACmdAdd: add a command to an XPA command public access point</A></H2> + +<!-- =section xpacmdadd SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + XPACmd XPACmdAdd(XPA xpa, char *name, char *help, + int (*send_callback)(), + void *send_data, char *send_mode, + int (*rec_callback)(), + void *rec_data, char *rec_mode); +</PRE> +</B> + +<!-- =section xpacmdadd DESCRIPTION --> +<P> +Add a command to an XPA command access point. The XPA argument specifies the +XPA struct returned by a call to XPANewCmd(). The name argument is the +name of the command. The other arguments function identically to the +arguments in the XPANew() command, i.e., the send_callback and rec_callback +routines have identical calling sequences to their XPANew() counterparts, +with the exceptions noted below. + +<P> +When help is requested for a command access point using: +<PRE> + xpaget -h class:name +</PRE> +<P> +all of the command help strings are listed. To get help for a given +command, use: +<PRE> + xpaget -h class:name cmd +</PRE> +<P> +Also, the acl keyword in the send_mode and receive_mode strings is +global to the access point, not local to the command. Thus, the value +for the acl mode should be the same in all send_mode (or receive_mode) +strings for each command in a command access point. (The acl for +send_mode need not be the same as the acl for receive_mode, though). + +<!-- =defdoc xpacmddel xpacmddel 3 --> + +<!-- =section xpacmddel NAME --> +<H2><A NAME="xpacmddel">XPACmdDel: remove a command from an XPA command public access point</A></H2> + +<!-- =section xpacmddel SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + void XPACmdDel(XPA xpa, XPACmd cmd); +</PRE> +</B> + +<!-- =section xpacmddel DESCRIPTION --> +<P> +This routine removes a command from the list of available commands in +a given XPA. That command will no longer be available for processing. + +<!-- =defdoc xpainfonew xpainfonew 3 --> + +<!-- =section xpainfonew NAME --> +<H2><A NAME="xpainfonew">XPAInfoNew: define an XPA info public access point</A></H2> + +<!-- =section xpainfonew SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + XPA XPAInfoNew(char *class, char *name, + int (*info_callback)(), + void *info_data, char *info_mode); +</PRE> +</B> + +<!-- =section xpainfonew DESCRIPTION --> +<P> +[NB: this is an experimental interface, new to XPA 2.0, whose value +and best use is evolving.] + +<P> +A program can register interest in receiving a short message about a +particular topic from any other process that cares to send such a +message. Neither has to be an XPA server. For example, if a user +starts to work with a new image file called new.fits, she might +wish to alert interested programs about this new file by sending a +short message using xpainfo: +<PRE> + xpainfo IMAGEFILE /data/new.fits +</PRE> + +<P> +In this example, each process that has used the XPAInfoNew() call to +register interest in messages associated with the identifier IMAGEFILE +will have its info_callback() executed with the following calling +sequence: +<PRE> + int info_cb(void *info_data, void *call_data, char *paramlist) + { + XPA xpa = (XPA)call_data; + } +</PRE> +<P> +The arguments passed to this routine are equivalent to those sent in +the send_callback() routine. The main difference is that there is no +buf sent to the info callback: this mechanism is meant for short +announcement of messages of interest to many clients. + +<P> +The mode string is of the form: "key1=value1,key2=value2,..." +The following keywords are recognized: +<PRE> + key value default explanation + ------ -------- -------- ----------- + acl true/false true enable access control +</PRE> +<P> +Because no buf is passed to this callback, the usual buf-related keywords +are not applicable here. + +<P> +The information sent in the parameter list is arbitrary. However, we +envision sending information such as file names or XPA access points +from which to collect more data. Note that the xpainfo program and +the XPAInfo() routine that cause the info_callback to execute do not +wait for the callback to complete before returning. + +<!-- =defdoc xpafree xpafree 3 --> + +<!-- =section xpafree NAME --> +<H2><A NAME="xpafree">XPAFree: remove an XPA public access point</A></H2> + +<!-- =section xpafree SYNOPSIS --> +<PRE> +<B> + #include <xpa.h> + + int XPAFree(XPA xpa); +</B> +</PRE> + +<!-- =section xpafree DESCRIPTION --> +<P> +Remove the specified XPA public access point from the name server and +free all associated storage. Note that removal from the name server +happens automatically when the process terminates, so this call is not +generally needed. It is used when public access points are being +defined temporarily and then destroyed when no longer needed. For +example, ds9 temporarily creates a public access point when it +loads a new image for display and destroys it when the image is +unloaded. + +<!-- =defdoc xpamainloop xpamainloop 3 --> + +<!-- =section xpamainloop NAME --> +<H2><A NAME="xpamainloop">XPAMainLoop: optional main loop for XPA</A></H2> + +<!-- =section xpamainloop SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + void XPAMainLoop(); +</PRE> +</B> + +<!-- =section xpamainloop DESCRIPTION --> +<P> +Once XPA access points have been defined, a program must enter an +event loop to watch for requests from external programs. This can be +done in a variety of ways, depending on whether the event loop is +processing events other than XPA events. In cases where there are no +non-XPA events to be processed, the program can simply call the +XPAMainLoop() event loop. This loop is implemented essentially as +follows (error checking is simplified in this example): +<PRE> + FD_ZERO(&readfds); + while( XPAAddSelect(NULL, &readfds) ){ + if( sgot = select(swidth, &readfds, NULL, NULL, NULL) >0 ) + XPAProcessSelect(&readfds, 0); + else + break; + FD_ZERO(&readfds); + } +</PRE> +<P> +The XPAAddSelect() routine sets up the select() readfds variable so +that select() will wait for I/O on all the active XPA channels. It +returns the number of XPAs that are active; the loop will end when +there are no active XPAs. The standard select() routine is called to +wait for an external I/O request. Since no timeout struct is passed +in argument 5, the select() call hangs until there is an external +request. When an external I/O request is made, the XPAProcessSelect() +routine is executed to process the pending requests. In this routine, +the maxreq value determines how many requests will be processed: if +maxreq <=0, then all currently pending requests will be processed. +Otherwise, up to maxreq requests will be processed. (The most usual +values for maxreq is 0 to process all requests.) + +<P> +If a program has its own Unix select() loop, then XPA access points can +be added to it by using a variation of the standard XPAMainLoop: +<PRE> + XPAAddSelect(xpa, &readfds); + [app-specific ...] + if( select(width, &readfds, ...) ){ + XPAProcessSelect(&readfds, maxreq); + [app-specific ...] + FD_ZERO(&readfds); + } +</PRE> +<P> +XPAAddSelect() is called before select() to add the access points. +If the first argument is NULL, then all active XPA access points +are added. Otherwise only the specified access point is added. +After select() is called, the XPAProcessSelect() routine can be called +to process XPA requests. Once again, the maxreq value determines how +many requests will be processed: if maxreq <=0, then all currently +pending requests will be processed. Otherwise, up to maxreq requests +will be processed. + +<P> +XPA access points can be added to +<A HREF="./xt.html">Xt event loops</A> (using XtAppMainLoop()) +and +<A HREF="./tcl.html">Tcl/Tk event loops</A> (using vwait and the Tk loop). +When using XPA with these event loops, you only need to call: +<PRE> +int XPAXtAddInput(XtAppContext app, XPA xpa) +</PRE> +or +<PRE> + int XPATclAddInput(XPA xpa) +</PRE> +respectively before entering the loop. + +<!-- =defdoc xpapoll xpapoll 3 --> + +<!-- =section xpapoll NAME --> +<H2><A NAME="xpapoll">XPAPoll: execute existing XPA requests</A></H2> + +<!-- =section xpapoll SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + int XPAPoll(int msec, int maxreq); +</PRE> +</B> + +<!-- =section xpapoll DESCRIPTION --> +<P> +It is sometimes desirable to implement a polling loop, i.e., where one +checks for and processes XPA requests without blocking. For this +situation, use the XPAPoll() routine: +<PRE> + XPAPoll(int msec, int maxreq); +</PRE> +<P> +The XPAPoll() routine will perform XPAAddSelect() and select(), but with a +timeout specified in millisecs by the msec argument. If one or more +XPA requests are made before the timeout expires, the XPAProcessSelect() +routine is called to process those requests. The maxreq value determines +how many requests will be processed: if maxreq < 0, then no events are +processed, but instead, the return value indicates the number of events +that are pending. If maxreq == 0, then all currently pending requests +will be processed. Otherwise, up to maxreq requests will be processed. +(The most usual values for maxreq are 0 to process all requests and 1 +to process one request). + +<!-- =defdoc xpaatexit xpaatexit 3 --> + +<!-- =section xpaatexit NAME --> +<H2><A NAME="xpaatexit">XPAAtExit: install exit handler</A></H2> + +<!-- =section xpaatexit SYNOPSIS --> +<PRE> +<B> + #include <xpa.h> + + void XPAAtExit(void); +</B> +</PRE> + +<!-- =section xpaatexit DESCRIPTION --> +<P> +XPAAtExit() will install an exit handler using atexit() to run XPAFree on all +XPA access points. This might be useful in cases where Unix sockets are being +used: if an explicit call to XPAFree() is not made by the program, the Unix +socket file will not be deleted immediately without an atexit handler. (NB: this +call should not be made in a Tcl/Tk application. Accessing the Tcl native file +system after Tcl has shut down all file systems causes the Tcl/Tl program to +crash). + +<!-- =defdoc xpacleanup xpacleanup 3 --> + +<!-- =section xpacleanup NAME --> +<H2><A NAME="xpacleanup">XPACleanup: release reserved XPA memory</A></H2> + +<!-- =section xpacleanup SYNOPSIS --> +<PRE> +<B> + #include <xpa.h> + + void XPACleanup(void); +</B> +</PRE> + +<!-- =section xpacleanup DESCRIPTION --> +<P> +When XPA is initialized, it allocates a small amount of memory for the +access control list, temp directory path, and reserved commands. This +memory is found by valgrind to be "still reachable", meaning that "your +program didn't free some memory it could have". Calling the +XPACleanup() routine before exiting the program will free this memory +and make valgrind happy. + +<!-- =defdoc xpamacros xpamacros 3 --> + +<!-- =section xpamacros NAME --> +<H2><A NAME="macros">XPA Server Callback Macros</A></H2> + +<!-- =section xpamacros SYNOPSIS --> +<B> +<PRE> + #include <xpa.h> + + xpa_class, xpa_name, xpa_method, xpa_cmdfd, xpa_datafd, + xpa_sendian, xpa_cendian +</PRE> +</B> + +<!-- =section xpamacros DESCRIPTION --> +<P> +Server routines have access to information about the XPA being called via +the following macros (each of which takes the xpa handle as an argument): +<PRE> + macro explanation + ------ ----------- + xpa_class class of this xpa + xpa_name name of this xpa + xpa_method method string (inet or local connect info) + xpa_cmdfd fd of command socket + xpa_datafd fd of data socket + xpa_sendian endian-ness of server ("little" or "big") + xpa_cendian endian-ness of client ("little" or "big" +</PRE> +<P> +The argument to these macros is the call_data pointer that is passed +to the server procedure. This pointer should be type case to XPA +in the server routine: +<PRE> + XPA xpa = (XPA)call_data; +</PRE> + +<P> +The most important of these macros is xpa_datafd(). A server routine +that sets "fillbuf=false" in receive_mode or send_mode can use this +macro to perform I/O directly to/from the client, rather than using +buf. + +<P> +The xpa_cendian and xpa_sendian macros can be used together to determine +if the data transferred from the client is byte swapped with respect +to the server. Values for these macros are: "little", "big", or "?". +In order to do a proper conversion, you still need to know the format +of the data (i.e., byte swapping is dependent on the size of the data +element being converted). + +<!-- =defdoc xparace xparace 3 --> + +<!-- =section xparace NAME --> +<H2><A NAME="race">XPA Race Conditions</A></H2> + +<!-- =section xparace SYNOPSIS --> +Potential XPA race conditions and how to avoid them. + +<!-- =section xparace DESCRIPTION --> +<P> +Currently, there is only one known circumstance in which XPA can get +(temporarily) deadlocked in a race condition: if two or more XPA +servers send messages to one another using an XPA client routine such +as XPASet(), they can deadlock while each waits for the other server +to respond. (This can happen if the servers call XPAPoll() with a +time limit, and send messages in between the polling call.) The +reason this happens is that both client routines send a string to the +other server to establish the handshake and then wait for the server +response. Since each client is waiting for a response, neither is able +to enter its event-handling loop and respond to the other's +request. This deadlock will continue until one of the timeout periods +expire, at which point an error condition will be triggered and the +timed-out server will return to its event loop. + +<P> +Starting with version 2.1.6, this rare race condition can be +avoided by setting the XPA_IOCALLSXPA environment variable for servers +that will make client calls. Setting this variable causes all XPA +socket IO calls to process outstanding XPA requests whenever the +primary socket is not ready for IO. This means that a server making a +client call will (recursively) process incoming server requests while +waiting for client completion. It also means that a server callback +routine can handle incoming XPA messages if it makes its own XPA call. +The semi-public routine oldvalue=XPAIOCallsXPA(newvalue) can be used +to turn this behavior off and on temporarily. Passing a 0 will turn +off IO processing, 1 will turn it back on. The old value is returned +by the call. + +<P> +By default, the XPA_IOCALLSXPA option is turned off, because we judge +that the added code complication and overhead involved will not be +justified by the amount of its use. Moreover, processing XPA requests +within socket IO can lead to non-intuitive results, since incoming +server requests will not necessarily be processed to completion in the +order in which they are received. + +<P> +Aside from setting XPA_IOCALLSXPA, the simplest way to avoid this race +condition is to multi-process: when you want to send a client message, +simply start a separate process to call the client routine, so that +the server is not stopped. It probably is fastest and easiest to use +fork() and then have the child call the client routine and exit. But +you also can use either the system() or popen() routine to start one +of the command line programs and do the same thing. Alternatively, you +can use XPA's internal launch() routine instead of system(). Based on +fork() and exec(), this routine is more secure than system() because +it does not call /bin/sh. + +<P> +Starting with version 2.1.5, you also can send an XPAInfo() message with +the mode string "ack=false". This will cause the client to send a message +to the server and then exit without waiting for any return message from +the server. This UDP-like behavior will avoid the server deadlock when +sending short XPAInfo messages. + +<!-- =section xpaserver SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpanew SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpacmdnew SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpacmdadd SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpacmddel SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpainfonew SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpafree SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpacleanup SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpamainloop SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpapoll SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xpamacros SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =section xparace SEE ALSO --> +<!-- =text See xpa(n) for a list of XPA help pages --> +<!-- =stop --> + +<P> +<A HREF="./help.html">Go to XPA Help Index</A> + +<H5>Last updated: September 10, 2003</H5> +</BODY> +</HTML> |