diff options
Diffstat (limited to 'libtecla-1.6.1/html/gl_io_mode.html')
-rw-r--r-- | libtecla-1.6.1/html/gl_io_mode.html | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/libtecla-1.6.1/html/gl_io_mode.html b/libtecla-1.6.1/html/gl_io_mode.html new file mode 100644 index 0000000..98c15a7 --- /dev/null +++ b/libtecla-1.6.1/html/gl_io_mode.html @@ -0,0 +1,509 @@ +<head> +<title>Manual Page</title> +</head> +<body> +<pre> +<a href="gl_io_mode.html"><b>gl_io_mode</b></a> <a href="gl_io_mode.html"><b>gl_io_mode</b></a> + + + +</pre><h2>NAME</h2><pre> + gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line, + gl_handle_signal, gl_pending_io - How to use gl_get_line() from an + external event loop. + +</pre><h2>SYNOPSIS</h2><pre> + #include <libtecla.h> + + int gl_io_mode(GetLine *gl, GlIOMode mode); + + int gl_raw_io(GetLine *gl); + + int gl_normal_io(GetLine *gl); + + int gl_tty_signals(void (*term_handler)(int), + void (*susp_handler)(int), + void (*cont_handler)(int), + void (*size_handler)(int)); + + void gl_abandon_line(GetLine *gl); + + void gl_handle_signal(int signo, GetLine *gl, int ngl); + + GlPendingIO gl_pending_io(GetLine *gl); + + + +</pre><h2>DESCRIPTION</h2><pre> + The gl_get_line() function, which is documented separately in the + <a href="gl_get_line.html"><b>gl_get_line</b></a> man page, supports two different I/O modes. + These are selected by calling the gl_io_mode() function. + + + int gl_io_mode(GetLine *gl, GlIOMode mode); + + + The mode argument of this function specifies the new I/O mode, and must + be one of the following. + + + GL_NORMAL_MODE - Select the normal blocking-I/O mode. + In this mode gl_get_line() + doesn't return until either an error + occurs of the user finishes entering a + new line. This mode is the focus of + the <a href="gl_get_line.html"><b>gl_get_line</b></a> man page. + + GL_SERVER_MODE - Select non-blocking server I/O mode. + In this mode, since non-blocking + terminal I/O is used, the entry of + each new input line typically requires + many calls to gl_get_line() from + an external I/O-driven event loop. + This mode is the focus of this man + page. + + + Newly created GetLine objects start in normal I/O mode, so to switch to + non-blocking server mode requires an initial call to gl_io_mode(). + + +</pre><h2>SERVER I/O MODE</h2><pre> + In non-blocking server I/O mode, the application is required to have an + event loop which calls gl_get_line() whenever the terminal file + descriptor can do the type I/O that gl_get_line() is waiting for. To + determine which type of I/O gl_get_line() is waiting for, the applica- + tion calls the gl_pending_io() function. + + + GlPendingIO gl_pending_io(GetLine *gl); + + + The return value of this function is one of the following two enumer- + ated values. + + + GLP_READ - gl_get_line() is waiting to write a + character to the terminal. + + GLP_WRITE - gl_get_line() is waiting to read a + character from the keyboad. + + + If the application is using either the select() or poll() system calls + to watch for I/O on a group of file descriptors, then it should call + the gl_pending_io() function before each call to these functions to see + which direction of I/O it should tell them to watch for, and configure + their arguments accordingly. In the case of the select() system call, + this means using the FD_SET() macro to add the terminal file descriptor + either to the set of file descriptors to be watched for readability, or + the set to be watched for writability. + + As in normal I/O mode, the return value of gl_get_line() is either a + pointer to a completed input line, or NULL. However, whereas in normal + I/O mode a NULL return value always means that an error occurred, in + non-blocking server mode, NULL is also returned when gl_get_line() + can't read or write to the terminal without blocking. Thus in non- + blocking server mode, in order to determine when a NULL return value + signifies that an error occurred or not, it is necessary to call the + gl_return_status() function. If this function returns the enumerated + value, GLR_BLOCKED, as documented in the <a href="gl_get_line.html"><b>gl_get_line</b></a> man + page, this means that gl_get_line() is waiting for I/O, and no error + has occurred. + + When gl_get_line() returns NULL and gl_return_status() indicates that + this is due to blocked terminal I/O, the application should call + gl_get_line() again when the type of I/O reported by gl_pending_io() + becomes possible. The prompt, start_line and start_pos arguments of + gl_get_line() will be ignored on these calls. If you need to change + the prompt of the line that is currently being edited, then you can + call the gl_replace_prompt() function (documented in the + <a href="gl_get_line.html"><b>gl_get_line</b></a> man page) between calls to gl_get_line(). + + +</pre><h2>GIVING UP THE TERMINAL</h2><pre> + A complication that is unique to non-blocking server mode is that it + requires that the terminal be left in raw mode between calls to + gl_get_line(). If this weren't the case, the external event loop + wouldn't be able to detect individual key-presses, and the basic line + editing implemented by the terminal driver would clash with the editing + provided by gl_get_line(). What this means is that any time that the + terminal needs to be used for other things than entering a new input + line with gl_get_line(), it needs to be restored to a usable state. In + particular, whenever the process is suspended or terminated, the termi- + nal must be returned to a normal state. If this isn't done, then + depending on the characteristics of the shell that was used to invoke + the program, the user may end up with a hung terminal. To this end, the + gl_normal_io() function is provided for switching the terminal back to + the state that it was in when raw mode was last established. + + + int gl_normal_io(GetLine *gl); + + + What this function does is first flush any pending output to the termi- + nal, then move the cursor to the start of the terminal line which fol- + lows the end of the incompletely entered input line. At this point it + is safe to suspend or terminate the process, and it is safe for the + application to read and write to the terminal. To resume entry of the + input line, the application should call the gl_raw_io() function. + + + int gl_raw_io(GetLine *gl); + + + This function starts a new line, redisplays the partially completed + input line (if any), restores the cursor position within this line to + where it was when gl_normal_io() was called, then switches back to raw, + non-blocking terminal mode ready to continue entry of the input line + when gl_get_line() is next called. + + Note that in non-blocking server mode, if gl_get_line() is called after + a call to gl_normal_io(), without an intervening call to gl_raw_io(), + gl_get_line() will call gl_raw_mode() itself, and the terminal will + remain in this mode when gl_get_line() returns. + + +</pre><h2>SIGNAL HANDLING</h2><pre> + In the previous section it was pointed out that in non-blocking server + mode, the terminal must be restored to a sane state whenever a signal + is received that either suspends or terminates the process. In normal + I/O mode, this is done for you by gl_get_line(), but in non-blocking + server mode, since the terminal is left in raw mode between calls to + gl_get_line(), this signal handling has to be done by the application. + Since there are many signals that can suspend or terminate a process, + as well as other signals that are important to gl_get_line(), such as + the SIGWINCH signal, which tells it when the terminal size has changed, + the gl_tty_signals() function is provided for installing signal han- + dlers for all pertinent signals. + + + int gl_tty_signals(void (*term_handler)(int), + void (*susp_handler)(int), + void (*cont_handler)(int), + void (*size_handler)(int)); + + + What this does is use gl_get_line()'s internal list of signals to + assign specified signal handlers to groups of signals. The arguments of + this function are as follows. + + + term_handler - This is the signal handler that is to be + used to trap signals that by default + terminate any process that receives + them (eg. SIGINT or SIGTERM). + + susp_handler - This is the signal handler that is to be + used to trap signals that by default + suspend any process that receives them, + (eg. SIGTSTP or SIGTTOU). + + cont_handler - This is the signal handler that is to be + used to trap signals that are usually + sent when a process resumes after being + suspended (usually SIGCONT). Beware that there is + nothing to stop a user from sending one of these + signals at other times. + + size_handler - This signal handler is used to trap + signals that are sent to processes when + their controlling terminals are resized + by the user (eg. SIGWINCH). + + + These arguments can all be the same, if so desired, and you can specify + SIG_IGN (ignore this signal) or SIG_DFL (use the system-provided + default signal handler) instead of a function where pertinent. In par- + ticular, it is rarely useful to trap SIGCONT, so the cont_handler argu- + ment will usually be SIG_DFL or SIG_IGN. + + The gl_tty_signals() function uses the POSIX sigaction() function to + install these signal handlers, and it is careful to use the sa_mask + member of each sigaction structure to ensure that only one of these + signals is ever delivered at a time. This guards against different + instances of these signal handlers from simultaneously trying to write + to common global data, such as a shared sigsetjmp() buffer or a signal- + received flag. + + The signal handlers that are installed by this function, should call + the gl_handle_signal(). + + + void gl_handle_signal(int signo, GetLine *gl, int ngl); + + + The signo argument tells this function which signal it is being asked + to respond to, and the gl argument should be a pointer to the first + element of an array of ngl GetLine objects. If your application only + has one of these objects, just pass its pointer as the gl argument and + specify ngl as 1. + + Depending on the signal that is being handled, this function does dif- + ferent things. + + + Terminal resize signals (SIGWINCH) + If the signal indicates that the terminal was resized, then it arranges + for the next call to gl_get_line() to ask the terminal for its new size + and redraw the input line accordingly. In order that gl_get_line() be + called as soon as possible to do this, gl_handle_signal() also arranges + that the next call to gl_pending_io() will return GLP_WRITE. Thus if + the application waits for I/O in select() or poll(), then the applica- + tion needs to ensure that these functions will be reliably aborted when + a signal is caught and handled by the application. More on this below. + + +</pre><h2>Process termination signals.</h2><pre> + If the signal that was caught is one of those that by default termi- + nates any process that receives it, then gl_handle_signal() does the + following steps. + + 1. First it blocks the delivery of all signals that can be + blocked (ie. SIGKILL and SIGSTOP can't be blocked) + + 2. Next it calls gl_normal_io() for each of the ngl + GetLine objects. Note that this does nothing to any of the + GetLine objects that aren't currently in raw mode. + + 3. Next it sets the signal handler of the signal to its default, + process-termination disposition. + + 4. Next it re-sends the process the signal that was caught. + + 5. Finally it unblocks delivery of this signal, which + results in the process being terminated. + + +</pre><h2>Process suspension signals.</h2><pre> + If the default disposition of the signal is to suspend the process, the + same steps are executed as for process termination signals, except that + when the process is later resumed, gl_handle_signal() continues, and + does the following steps. + + 6. It re-blocks delivery of the signal. + + 7. It reinstates the signal handler of the signal to the one + that was displaced when its default disposition was substituted. + + 8. For any of the GetLine objects that were in raw mode when + gl_handle_signal() was called, gl_handle_signal() then + calls gl_raw_io(), to resume entry of the input lines on + those terminals. + + 9. Finally, it restores the signal process mask to how it + was when gl_handle_signal() was called. + + Note that the process is suspended or terminated using the original + signal that was caught, rather than using the uncatchable SIGSTOP and + SIGKILL signals. This is important, because when a process is suspended + or terminated, the parent of the process may wish to use the status + value returned by the wait() system call to figure out which signal was + responsible. In particular, most shells use this information to print a + corresponding message to the terminal. Users would be rightly confused + if when their process received a SIGPIPE signal, the program responded + by sending itself a SIGKILL signal, and the shell then printed out the + provocative statement, "Killed!". + + +</pre><h2>INTERRUPTING THE EVENT LOOP</h2><pre> + If a signal is caught and handled when the application's event loop is + waiting in select() or poll(), these functions will be aborted with + errno set to EINTR. When this happens the event loop should call + gl_pending_io(), before calling select() or poll() again. It should + then arrange for select() or poll() to wait for the type of I/O that + this reports. This is necessary, because any signal handler which calls + gl_handle_signal(), will frequently change the type of I/O that + gl_get_line() is waiting for. + + Unfortunately, if a signal arrives between the statements which config- + ure the arguments of select() or poll() and the calls to these func- + tions, then the signal will not be seen by these functions, which will + then not be aborted. If these functions are waiting for keyboard input + from the user when the signal is received, and the signal handler + arranges to redraw the input line to accomodate a terminal resize or + the resumption of the process, then this redisplay will be end up being + delayed until the user hits the next key. Apart from puzzling the user, + this clearly isn't a serious problem. However there is a way, albeit + complicated, to completely avoid this race condition. The following + steps illustrate this. + + 1. Block all of the signals that gl_get_line() catches, + by passing the signal set returned by gl_list_signals() to + sigprocmask(). + + 2. Call gl_pending_io() and set up the arguments of + select() or poll() accordingly. + + 3. Call sigsetjmp() with a non-zero savesigs argument. + + 4. Initially this sigsetjmp() statement will return zero, + indicating that control isn't resuming there after a matching + call to siglongjmp(). + + 5. Replace all of the handlers of the signals that gl_get_line() + is configured to catch, with a signal handler that first records + the number of the signal that was caught, in a file-scope variable, + then calls siglongjmp() with a non-zero value argument, to + return execution to the above sigsetjmp() + statement. Registering these signal handlers can conveniently be + done using the gl_tty_signals() function. + + 6. Set the file-scope variable that the above signal handler uses to + record any signal that is caught to -1, so that we can check + whether a signal was caught by seeing if it contains a valid signal + number. + + 7. Now unblock the signals that were blocked in step 1. Any signal + that was received by the process in between step 1 and now will + now be delivered, and trigger our signal handler, as will any + signal that is received until we block these signals again. + + 8. Now call select() or poll(). + + 9. When select() returns, again block the signals that were + unblocked in step 7. + + If a signal is arrived any time during the above steps, our signal han- + dler will be triggered and cause control to return to the sigsetjmp() + statement, where this time, sigsetjmp() will return non-zero, indicat- + ing that a signal was caught. When this happens we simply skip the + above block of statements, and continue with the following statements, + which are executed regardless of whether or not a signal is caught. + Note that when sigsetjmp() returns, regardless of why it returned, the + process signal mask is returned to how it was when sigsetjmp() was + called. Thus the following statements are always executed with all of + our signals blocked. + + 9. Reinstate the signal handlers that were displaced in step 5. + + 10. Check wether a signal was caught, by checking the file-scope + variable that the signal handler records signal numbers in. + + 11. If a signal was caught, send this signal to the application + again, and unblock just this signal, so that it invokes the + signal handler which we just reinstated in step 10. + + 12. Unblock all of the signals that were blocked in step 7. + + Since this is complicated, note that demo3.c includes a working example + of how to do this. The method used there however, is more general than + the above. What it provides is a wrapper function around select() which + encompasses steps 3 to 11. In this wrapper, rather than use + gl_list_signals() to figure out the signals to block, and and + gl_tty_signals() to assign and revert signal handlers, one of its argu- + ments is a sigset_t which specifies which signals to block and assign + signal handlers to. This function thus doesn't depend on gl_get_line() + and can thus be used in other situations where race-condition-free sig- + nal handling is required. + + +</pre><h2>SIGNALS CAUGHT BY GL_GET_LINE</h2><pre> + Since the application is expected to handle signals in non-blocking + server mode, gl_get_line() doesn't attempt to duplicate this when it is + being called. If one of the signals that it is configured to catch is + sent to the application while gl_get_line() is being called, + gl_get_line() reinstates the caller's signal handlers, then just before + returning, re-sends the signal to the process to let the application's + signal handler handle it. If the process isn't terminated by this sig- + nal, gl_get_line() returns NULL, and a following call to gl_return_sta- + tus() returns the enumerated value GLR_SIGNAL. + + +</pre><h2>ABORTING LINE INPUT</h2><pre> + Often, rather than letting it terminate the process, applications + respond to the SIGINT user-interrupt signal by aborting the current + input line. The way to do this in non-blocking server-I/O mode is to + not call gl_handle_signal() when this signal is caught, but instead to + call the gl_abandon_line(). + + + void gl_abandon_line(GetLine *gl); + + + This function arranges that when gl_get_line() is next called, it first + flushes any pending output to the terminal, then discardes the current + input line, outputs a new prompt on the next line, and finally starts + accepting input of a new input line from the user. + + +</pre><h2>SIGNAL SAFE FUNCTIONS</h2><pre> + Provided that certain rules are followed, the following functions can + have been written to be safely callable from signal handlers. Other + functions in this library should not be called from signal handlers. + + + gl_normal_io() + gl_raw_io() + gl_handle_signal() + gl_abandon_line() + + + In order for this to be true, all signal handlers that call these func- + tions must be registered in such a way that only one instance of any + one of them can be running at one time. The way to do this is to use + the POSIX sigaction() function to register all signal handlers, and + when doing this, use the sa_mask member of the corresponding sigaction + structure, to indicate that all of the signals who's handlers invoke + the above functions, should be blocked when the current signal is being + handled. This prevents two signal handlers from operating on a GetLine + object at the same time. + + To prevent signal handlers from accessing a GetLine object while + gl_get_line() or any of its associated public functions are operating + on it, all public functions associated with gl_get_line(), including + gl_get_line() itself, temporarily block the delivery of signals when + they are accessing GetLine objects. Beware that the only signals that + they block are the signals that gl_get_line() is currently configured + to catch, so be sure that if you call any of the above functions from + signal handlers, that the signals that these handlers are assigned to + are configured to be caught by gl_get_line() (see gl_trap_signal()). + + +</pre><h2>USING TIMEOUTS TO POLL</h2><pre> + If instead of using select() or poll() to wait for I/O, your applica- + tion just needs to get out of gl_get_line() periodically to briefly do + something else before returning to accept input from the user, this can + be done in non-blocking server mode by using the gl_inactivity_time- + out() function (see <a href="gl_get_line.html"><b>gl_get_line</b></a>), to specify that a + callback function that returns GLTO_CONTINUE should be called whenever + gl_get_line() has been waiting for I/O for more than a specified amount + of time. + + When this callback is triggered, gl_get_line() will return NULL, and a + following call to gl_return_status() will return GLR_BLOCKED. + + Beware that gl_get_line() won't return until the user hasn't typed a + key for the specified interval, so if the interval is long, and the + user keeps typing, gl_get_line() may not return for a while. In other + words there is no guarantee that it will return in the time specified. + + +</pre><h2>THE SERVER DEMO PROGRAM</h2><pre> + The demo3 program that is distributed with the library, provides a + working example of how to use non-blocking server I/O mode in a real + program. As far as the user is concerned, this program operates + identically to the main demo program (called demo), except that whereas + the main demo program uses the normal blocking I/O mode, demo3 using + non-blocking I/O and an external event loop. The source code can be + found in demo3.c, and the comments therein explain the various steps. + + +</pre><h2>FILES</h2><pre> + libtecla.a - The tecla library + libtecla.h - The tecla header file. + + +</pre><h2>SEE ALSO</h2><pre> + <a href="libtecla.html"><b>libtecla</b></a>, <a href="gl_get_line.html"><b>gl_get_line</b></a>, <a href="tecla.html"><b>tecla</b></a>, <a href="ef_expand_file.html"><b>ef_expand_file</b></a>, + <a href="cpl_complete_word.html"><b>cpl_complete_word</b></a>, <a href="pca_lookup_file.html"><b>pca_lookup_file</b></a> + + +</pre><h2>AUTHOR</h2><pre> + Martin Shepherd (mcs@astro.caltech.edu) + + + + <a href="gl_io_mode.html"><b>gl_io_mode</b></a> +</pre> +</body> |