<<< DOCD$:[NOTES$LIBRARY]SCT-RAVEN.NOTE;1 >>> -< The SCT Raven conference >- ================================================================================ Note 851.0 RAVEN_FT1: Java fork/exec enhancements No replies EVMS::RCASE_SMITH "{VDE SCT}" 299 lines 2-JUL-1997 16:39 -------------------------------------------------------------------------------- <<< STAR::NOTESD$:[NOTES$LIBRARY]SCT-RAVEN.NOTE;1 >>> -< SCT Conference for Raven VAX >- ================================================================================ Note 315.0 CRTL -- vfork/exec enhancements for Java port No replies STAR::RCASE_SMITH 290 lines 2-JUL-1997 16:37 -------------------------------------------------------------------------------- $! Project: CRTL/ACRTL $! Project Leader: Duane Smith $! Development Stream: Raven FT1 $! Checkin Type: Enhancements $! $! $! Problem Overview: $! $! Those engineers working on porting the Java environment to $! OpenVMS report a problem pertaining to the lack of real fork() $! function on VMS. $! $! In the original code the parent process communicates with the child $! process via the pipes established by the parent. Once fork, the child $! process redirects its standard channels (stdin, stdout and stderr) to $! the pipes while the parent process watches the pipes for child's stdout $! and stderr once the child is spawned by the call to exec. $! $! The Unix's scheme is as follows: $! $! parent: $! create pipes for stdin, stdout, stderr $! fork $! child: $! remap(0 to the pipe for stdin); $! remap(1 to the pipe for stdout); $! remap(2 to the pipe for stderr); $! exec $! exit $! parent: $! watch the pipes for child's stdout and stderr $! $! On Unix the code between the fork() and exec() is executed in the $! context of child's process so that after the new child is fork, the $! program can remap stdin, stdout, stderr to the pipes on behalf the $! child process before the call to exec(). $! $! On VMS, vfork() doesn't really create a new child until the exec(), $! so you are still in the parent's context. Therefore, in order to $! communicate through the pipes, the parent and the child must agree $! about the numbers of file descriptor which will be associated with $! the pipes. The parent must redirect the pipes to the predefined file $! descriptors before the call to exec() while the child must redirect $! its standard channels to the predefined file descriptors by itself. $! $! The VMS's scheme is as follows: $! $! #define STDIN_FD N $! #define STDOUT_FD N+1 $! #define STDERR_FD N+2 $! $! parent: $! create pipes for stdin, stdout, stderr $! remap(pipe for stdin to STDIN_FD); $! remap(pipe for stdout to STDOUT_FD); $! remap(pipe for stderr to STDERR_FD); $! fork $! exec $! child: $! remap(stdin to STDIN_FD); $! remap(stdout to STDOUT_FD); $! remap(stderr to STDERR_FD); $! exit $! parent: $! watch STDOUT_FD and STDERR_FD for child's output $! $! The problem is that there is no control of the child in the Java $! environment - the child "could be zip.exe, javac, directory, search, $! or a user application". $! $! $! After adding the ability to specify user-defined files for the child' $! standard streams (CRTL_INTERNAL 1751), the parent process can specify $! a pipe to be associated with the child's stdin. In this case in order $! to cause the child to terminate, the parent process must write an $! end-of-file message to the mailbox serving as the SYS$INPUT for the $! child' subprocess. $! $! If the mailbox is not a pipe, the write() function called with zero $! nbytes parameter sends the end-of-file message to the mailbox, but $! for a pipe the only way to wrire an end-of-file message to the mailbox $! is to close the pipe. $! $! If the possibility to write the end-of-file message to a mailbox $! existed, the parent process would reuse the pipe to communicate with $! other subprocesses without having to close/open the pipe. $! $! $! Diagnosis: $! $! As can be seen from the above, the root of the problem is that the $! CRTL is unable to switch process context to the child process after $! the call to [v]fork. This support cannot be added to the CRTL without $! changing the VMS kernel as it was done for the POSIX implementation $! on OpenVMS. $! $! $! Cure: $! $! To solve the particular problem of communication through the pipes, $! the decision was made to introduce a special routine, called $! decc$set_child_standard_streams(). This routine allows the user to tell $! the CRTL to associate certain files with child' standard channels before $! the exec() request is issued. $! $! The routine accepts three parameters, as follows: $! $! int decc$set_child_standard_streams(stdin_fd, stdout_fd, stderr_fd); $! $! where $! $! stdin_fd - file descriptor to be associated with the child's stdin $! stdout_fd - file descriptor to be associated with the child's stdout $! stderr_fd - file descriptor to be associated with the child's stderr $! $! Each of stdin_fd, stdout_fd, stderr_fd may be either a non-negative file $! descriptor or -1 in which case this particular stream is reverted back $! to the standard mapping (0 for stdin, 1 for stdout and 2 for stderr). $! $! Each standard channel can be mapped/remapped separately. $! $! Device associated with the file descriptor specified in the call $! to decc$set_child_standard_streams() must be a mailbox. $! $! stdin_fd must allow read access to the mailbox while stdout_fd and $! stderr_fd must allow write access to the mailbox. $! $! stdout_fd and stderr_fd can be the same. $! $! The routine returns the number of channels which were set to a $! user-specified files, including the channels which were explicitly $! set to their standard targets (0 for stdin, 1 for stdout and 2 for stderr). $! $! The returning value may be zero for a $! decc$set_child_standard_streams(-1, -1, -1) call, for example. $! $! If an error was encountered, -1 is returned and errno is set to EBADF. $! $! Once established via the call to decc$set_child_standard_streams(), the $! mapping of child' standard channels remains in effect until explicitly $! disabled by one of the following calls: $! $! decc$set_child_standard_streams(-1, -1, -1); $! or $! decc$set_child_standard_streams(0, 1, 2); $! $! New VMS's scheme is as follows: $! $! parent: $! /* create pipes for stdin, stdout, stderr */ $! int fdin[2], fdout[2], fderr[2]; $! pipe(fdin); $! pipe(fdout); $! pipe(fderr); $! /* call decc$set_child_standard_streams() */ $! decc$set_child_standard_streams(fdin[0], fdout[1], fderr[1]); $! /* spawn the child */ $! fork $! exec $! child: $! read from stdin, write to stdout/stderr $! exit $! parent: $! write to fdin[1], read from fdout[0] and fderr[0] for child's $! stdout and stderr $! $! Passing non-standard file descriptors to be associated with the child' $! standard channels, are accomplished by $! $! 1) specifying appropriate input-file and output-file parameters in $! the call to LIB$SPAWN $! $! 2) sending the files associated with the file descriptors specified $! in the call to decc$set_child_standard_streams() as files number $! 0, 1, 2, respectively, while sending the open files database to $! the child $! $! The second step is performed only if the child is a VMS image (after the $! work done for CRTL_INTERNAL 1749, the exec family of function can activate $! either a VMS image or a DCL command file). Therefore, because LIB$SPAWN $! does not allow to specify a file for SYS$ERROR, in case of a command $! procedure third parameter of decc$set_child_standard_streams() does not $! affect the subprocess' SYS$ERROR. $! $! For a VMS image, both 1) and 2) are done so that the child inherits $! all three standard files using the CRTL inheritance mechanism (if this $! is an image written in C, of course). $! $! Original file descriptor specified in the call to $! decc$set_child_standard_streams() is not sent to the child because $! it becomes one of the child' stdin, stdout or stderr. $! $! For example, after the call $! $! decc$set_child_standard_streams(10, -1, -1) $! $! the parent's file # 10 is sent to the child as the file # 0 *rather than* $! sending it as the file # 10. $! $! Implication of specifying non-zero input-file parameter in LIB$SPAWN call $! is that the parent process must perform some action to cause the child $! process to terminate. $! $! From the description of LIB$SPAWN: $! $! If both command-string and input-file are present, the subprocess $! will first execute command-string and then read from input-file. $! [...] If input-file is specified, the subprocess will be terminated $! by either a LOGOUT command or an end-of-file. $! $! So, after performing the DCL command specified as the command-string $! argument (which is either the "RUN image" or "@command-file"), the child $! process will be waiting for the input from SYS$INPUT. $! $! To cause the child process to exit, the parent process can either $! close the pipe (in which case the end-of-file message is written to $! the mailbox) or call new decc$write_eof_to_mbx() routine introduced $! in CRTL_INTERNAL 1760. $! $! After the call to decc$write_eof_to_mbx(), the parent can reuse the $! pipe for communication with another child process. $! $! The decc$set_child_standard_streams() routine stores specified file $! descriptors in a thread-specific memory, so that different threads $! can specify different mapping for child' standard channels. $! $! $! A new routine called decc$write_eof_to_mbx() was introduced. $! $! The routine signature is: $! $! int decc$write_eof_to_mbx( int fd ); $! $! where $! $! fd is the file descriptor associated with the mailbox $! $! After checking that fd is a valid file descriptor associated with $! the mailbox device, the routine issues SYS$QIOW request with the $! IO$_WRITEOF|IO$M_NOW function. $! $! If SYS$QIOW succeeds, a zero status is returned, otherwise -1 is $! returned and errno is set to the appropriate value for the status $! returned by SYS$QIOW. If device is not a mailbox, -1 is returned $! and errno is set to EINVAL. $! $! $! $! Platforms affected: $! $! Both VAX and Alpha at this time $! $! $! Modules: $! $! [CRTL]CSHRXFR.MAR [ACRTL]ADECC$SHR.OPT $! [CRTL]DECC$SHRVEC.DAT [ACRTL]ADECC$SHRVEC.DAT $! [CRTL]F_ENVIRON.C [ACRTL]F_ENVIRON.C $! [CRTL]F_RECORDIO.C [ACRTL]F_RECORDIO.C $! [CRTL]RTLDEFS.SRC [ACRTL]RTLDEFS.SRC $! [CRTL]UNIX.C [ACRTL]UNIX.C $! [CRTL]VFORK.C [ACRTL]VFORK.C $! $! $! Affected Images (if feasible): $! $! [SYSLIB]DECC$SHR.EXE $! $! $! Impact/Risks: $! $! Low $! $! $! How this change was Tested: $! $! Tests CRTL1751A, CRTL1751B, CRTL1751C, and CRTL1760 were added. $! $! $! Associated QARs,CLDs,SPRs: $! $! CRTL_INTERNAL 1751, 1760 $! $! $! (Optional) Comments: None. $! $! (Optional) Special Build Instructions: None. $! $! VAX differences located at STAR::WORK25:[RCASE_SMITH.CHECKIN] $! ALPHA differences located at EVMS::WORK8:[RCASE_SMITH.CHECKIN]