/* * processes.sli * * This file is part of NEST. * * Copyright (C) 2004 The NEST Initiative * * NEST is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * NEST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NEST. If not, see <http://www.gnu.org/licenses/>. * */ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% This is the SLI-library defining wrapper-routines %% for the functions defined in module "processes.{h|cc}". %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /processes /SLI ($Revision: 10020 $) provide-component /processes /C++ (1.48) require-component /* BeginDocumentation Name: is_mpi - flag in statusdict indicating whether interpreter was compiled with MPI Synopsis: statusdict/is_mpi :: -> boolean Description: The C++ startup of a SLI interpreter which is not compiled with MPI support may be completely unaware of distributed computing and may therefore not define the /is_mpi flag. In order to simplify user code, processes.sli sets /is_mpi to false if it is not defined. Author: Diesmann FirstVersion: 090412 SeeAlso: statusdict */ statusdict /is_mpi known not {statusdict /is_mpi false put} if %-----------------------------------------------------------------------+ %This will make the C++-Functions defined in file processes.h typesafe: | %For a documentation of these functions, see file processes.h! | %-----------------------------------------------------------------------+ % ---"waitPID"--- /waitPID_i_b /waitPID load def % rename waitPID to waitPID_ib /waitPID trie [/integertype /booltype] /waitPID_i_b load addtotrie def % --------------- % ----"kill"----- /kill_i_i /kill load def % rename kill to kill_ii /kill_i_l { dup signaldict exch known %do we know the name of the signal? {signaldict exch get kill_i_i} %yes, so send it, using "kill_ii" {/kill /UnknownSignal raiseerror} %no, so raise error. ifelse } bind def /kill trie [/integertype /integertype] /kill_i_i load addtotrie [/integertype /literaltype] /kill_i_l load addtotrie def % --------------- % ---"available"--- /available_is /available load def % rename available to available_is /available trie [/istreamtype] /available_is load addtotrie def % --------------- % ---"mkfifo"--- /mkfifo_s /mkfifo load def % rename mkfifo to mkfifo_s /mkfifo trie [/stringtype] /mkfifo_s load addtotrie def % --------------- % ---"setNONBLOCK"--- /setNONBLOCK_is_b /setNONBLOCK load def % rename setNONBLOCK to setNONBLOCK_Ib /setNONBLOCK trie [/istreamtype /booltype] /setNONBLOCK_is_b load addtotrie def % --------------- % ---"fork"--- /fork_ /fork load def % rename fork to fork_ /fork { % first check if forks are allowed: statusdict /argv get First (/) breakup Last (sli) neq statusdict /have_mpi get and {% this is not a sli executable, and MPI has been linked! M_ERROR (fork) (MPI has been linked to this executable, and MPI does not support forks.) (If you need system calls, you may either\n) join ( * call the "sli" executable (it is never linked to MPI), or\n) join ( * compile NEST without MPI support.) join message /fork /ForkDisallowed raiseerror } if fork_ } bind def % --------------- %--------------------------------------------------------------------------- /* BeginDocumentation Name: spoon - execute a parallel SLI-process Synopsis: Command spoon -> Child_PID %for parent process -> quit Command %for child process Description: Basic process management: execute a parallel SLI-process Parameters: In: Command (any): (However, Command is most likely to be a literal procedure...) Command will be executed by the child process, which will terminate after execution. It will be ignored by the parent process. Out: For parent process: Child_PID (integer): The child's process-ID. For child process: none. Examples: { (I am the child!) = } spoon (I am the parent!) = Try this several times. You will notice the child message to appear before or after the parent message "by chance". (Even after the parent's I-prompt...) Bugs: Author: R Kupper FirstVersion: Mar 18 1999 Remarks: A parallel process is forked. Parent and child will execute in parallel. There is no way to know which will start being executed first. Child inherits all open files, including stdin and stdout, from parent! If a SLI error is raised inside the child process, an error message is issued and the process is terminated, in oder not to leave two interactive SLI processes waiting for user input. A copy of the error message is printed to cerr, in case the standrad message stream M_OUT is redirected to a pipe (i.e. by the spawn command). Procedure: Call fork. Check if you are parent/child by test of (PID eq 0). If parent: discard (pop) Command, leave: (child)PID. If child : discard (pop) 0-PID, execute Command, quit. SeeAlso: fork */ /spoon trie [/anytype] %stack: Command { fork %stack: Command PID. (PID is 0 for child, Child_PID for parent) dup 0 eq %stack: Command PID true/false. % if child: { %stack: Command PID pop %stack: Command {exec} stopped %protect from errors! {% an error occured. % define error message: /errormessage (SLI CHILD PROCESS ) getPID cvs join ( CAUSED AN ERROR AND WILL BE TERMINATED:\n/) join errordict /errorname get % if it is a system error, get proper description: dup /SystemError eq {pop errordict /sys_errname get} if cvs join ( in ) join errordict /commandname get cvs join def %print it: M_FATAL (spoon) errormessage message %print it again to cerr, in case other stream is redirected (spawn!) /M_OUT cerr def M_FATAL (spoon) errormessage message } if quit } % if parent: { %stack: Command PID exch pop %stack: PID. } ifelse } bind addtotrie def %---------------------------------------------------------------------------- /* BeginDocumentation Name: sysexec - Transfer control to a UNIX-command Synopsis: CommandArray sysexec -> - CommandString sysexec -> - Description: Transfer control to a UNIX-Command. The command may be executed in a specified program environment. The command may be given either as a string containing the command name and all options, or as an array of strings, specifying the command name and every single option. Alternatives: Functions sysexec_a for arrays, sysexec_s for strings (both undocumented) -> behaviour and synopsis are the same. Parameters: In : CommandArray (array of strings): An array containing the command to execute. The first element is interpreted as the command, the remaining elements as it's parameters. CommandString (string) : A standard UNIX command string, containing the command name and it's parameters to execute. Out: -whatever will be will be- Examples: These two calls are equivalent: 1. [ (ls) (-l) (/home/kupper) ] sysexec 2. (ls -l /home/kupper) sysexec This call is NOT equivalent to the above (see remarks below): 3. [ (ls) (-l /home/kupper) ] sysexec 4. {(ls -l /home/kupper) sysexec} spoon Look at the program environment of the new command: 5. {(printenv) sysexec} spoon It was inherited from the SLI process. Bugs: Will break down if given an array containing non-strings. This call is NOT quivalent to example 3, as one should expect: (ls "-l /home/kupper") sysexec. The reason is the breakup-command not recognizing ". Author: R Kupper FirstVersion: Mar 18 1999 Remarks: A call to sysexec WILL NOT RETURN, as control is literally transfered to the UNIX command. Given an array of strings, each element is treated as -one- parameter. That's why example 3. is equivalent to the UNIX-call ls "-l /home/kupper". SeeAlso: spoon, breakup, environment */ /sysexec_s { ( ) 0 trim ( ) breakup sysexec_a } bind def /sysexec [/arraytype] /sysexec_a load def /sysexec [/stringtype] /sysexec_s load def %---------------------------------------------------------------------------- /* BeginDocumentation Name: wait - Wait for any child process to terminate Synopsis: NoHangFlag wait -> Status NormalExitFlag PIDout -> 0 Description: Wait is the same as "waitPID", but it waits for any child process. Alternatives: Function wait_b (undocumented) -> behaviour and synopsis are the same, except that no warnings or error messages are thrown. Parameters: In : NoHangFlag(boolean): -see waitPID- Out: Status(integer) NormalExitFlag(boolean) PIDout(integer): -see waitPID- Examples: false wait %wait for the next child to terminate Bugs: Author: R Kupper FirstVersion: Apr 23 1999 Remarks: This is not a SLI-interface to the system's "wait()"-function, but a thin wrapper to waitPID. The behaviour is exactly the same, however. SeeAlso: waitPID, spawn, system, spoon, fork */ /wait_b { -1 exch waitPID } bind def /wait trie [/booltype] /wait_b load addtotrie def %---------------------------------------------------------------------------- /* BeginDocumentation Name: system - execute a UNIX command in a parallel process. Synopsis: CommandString system } { Flag=0 or omitted: CommandArray system } { Status NormalExitFlag } -> { CommandString Flag system } { Flag=1: - CommandArray Flag system } { } { Flag=2: ChildPID CommandString Flag Precode system } CommandArray Flag Precode system } Description: "system" creates a child process of the current SLI process and executes the given command in that child process. Depending on the value of "Flag", the parent process will wait for the child to terminate or continue execution in parallel. The command may be given either as a string containing the command name and all options, or as an array of strings, specifying the command name and every single option. If you are interested in communicating to the child process via pipes, see "spawn" or "shpawn". Alternatives: Functions system_as_i_p with array or string and integer(flag) and precode, system_as_di_i_p with array or string and dictionary followed by integer(flag) and precode (both undocumented) -> behaviour and synopsis are the same. Parameters: In : CommandString(string)/CommandArray(array of string): The UNIX command to execute. Either a string containing the command and all parameters, or an array of strings, containing the command and the parameters in separate (see "sysexec"). Flag: 0: Wait for termination of child process. A flag indicating if the child process exited normally or abnormally, and the exit status is left on the stack. 1: Return immediately. This is the "no-care-mode". An orphaned grandchild process is created. I.e. a child is forked, which again forks a child and then exits. The orphaned grandchild is inherited by the init-process immediately, which will perform any waiting. This is a legal way of producing a child you don't have to wait for. (However, see remarks below.) - All ressources occupied by the child process will be automatically released with its termination. - No result is left on the stack. 2: Return immediately. Create a normal child process and return immediately, leaving it's PID on the stack. ! The parent has to call "wait" or "waitPID" in order to free the ressources occupied by the child process after it's termination ! The "Flag" parameter may be omitted in which case it defaults to zero. Precode(executable procedure): -This parameter is for internal use with the "spawn" command. However, it is documented here for there may be unexpected applications.- In "Precode" there can be supplied a sequence of SLI commands that will be executed by the child process prior to calling "sysexec". The main application is to instruct the child process to redirect it's standard input/output to a pipe, as does the "spawn" command (for a example, see the code of "spawn"). Out: NormalExitFlag(boolean): True, if the child terminated normally, i.e. by a call to exit() or by returning from main(). In that case, the exit code is reported in the Status argument (see below). False, if the child terminated due to a signal that was not caught. In that case, the number of that signal is reported in the Status argument (see below). Status(integer): If NormalExitFlag is true, this reports the child's exit code, i.e. the low-order eight bits of the status argument that the child passed to exit(), or the value the child process returned from main(). If NormalExitFlag is false, this reports the number of the signal that caused the termination of the child process. Look up this number in signaldict, to know what it means. ChildPID(integer): The process ID of the newly created child. Examples: 1. (ls) system % This is the same as: (ls) 0 system 2. (xterm) 1 system 3. %The follwing asks SLI to make itself, do something else in parallel %and report make's exit code: (make --silent) 2 system /MyChild Set <do anything in parallel> MyChild false waitPID %wait for child to terminate pop pop (make returned exit code: ) =only = Bugs: Be aware of citation-mark-bug in breakup (see "breakup") Author: R Kupper FirstVersion: Apr 23 1999 Remarks: The no-care-mode (Flag=2) gives no way to know about the child`s exit code. If you need to care about the exit code, use Flag=0, which will leave it on the stack, or use Flag=1 and get the exit code by calling "wait" or "waitPID". If you are interested in communicating to the child process via pipes, see "spawn" or "shpawn". To Do: It should be possible to spawn a parallel SLI Process. This should be supported by a future version. Actually, as this is the more general case, the spoon/system/spawn chain of commands could be restructured. SeeAlso: spawn, sysexec, spoon, fork, waitPID, wait, breakup, environment */ /system_as_i_p %as: array or string { << >> begin % use local variables (for switch) /PreCode Set % Store pre-sysexec-code in PreCode /Flag Set % Store flag in /Flag %stack: CommStr mark Flag 0 eq { {sysexec} exch prepend % stack: {CommStr sysexec} /PreCode load prepend % stack: {PreCode CommStr sysexec} spoon false waitPID pop end exit } case Flag 1 eq { {sysexec} exch prepend % stack: {CommStr sysexec} /PreCode load prepend % stack: {PreCode CommStr sysexec} /a Set % store { PreCode CommStr sysexec } in /a { {a} spoon } spoon false waitPID 3 npop % create orphaned child end exit } case Flag 2 eq { {sysexec} exch prepend % stack: {CommStr sysexec} /PreCode load prepend % stack: {PreCode CommStr sysexec} spoon end exit } case {Flag end /system /FlagOutOfRange raiseerror} switchdefault } bind def % define trie structure: /system [/arraytype ] {0 {} system_as_i_p} def /system [/stringtype ] {0 {} system_as_i_p} def /system [/arraytype /integertype ] { {} system_as_i_p} def /system [/stringtype /integertype ] { {} system_as_i_p} def /system [/arraytype /integertype /proceduretype] /system_as_i_p load def /system [/stringtype /integertype /proceduretype] /system_as_i_p load def %----------------------------------------------------------------------------- /* BeginDocumentation Name: dup2 - Duplicate a filestream's file descriptor onto another's Synopsis: istream1 istream2 dup2 -> - ostream1 ostream2 dup2 -> - Description: "dup2" is a wrapper to the "dup2()" UNIX system call. This is a low-level file operation. It does not operate on filestreams, but on the underlying UNIX file descriptors. Operation: 1. "dup2" gets the file descriptors fd1 and fd2, to which the two streams are bound. (See remarks below!) 2. System function dup2(fd1, fd2) is called. This means: o If fd1 equals fd2, nothing is done. o If not, the file associated with fd2, if any, is closed. fd2 is then attached to the same file as fd1. It refers to the same open file as fd1 and shares any locks. Thus, the values of fd1 and fd2 stay unchanged, but fd2 refers to a different file after the call. Respectively, C++-stream2 stays unchanged in every detail (buffer, value of undelying file descriptor, ...) but is actually connected to a different file. "dup2" will usually be called on "cin", "cout" or "cerr" after a "fork" or "spoon" and prior to calling "sysexec". This is used to redirect the standard channels of the newly created process to a file, pipe or fifo. For a typical call see examples below. In a UNIX environment, each process expects the standard channels stdin, stdout, stderr to be associated to three special file descriptors (usually 0, 1 and 2). Thus, if a parent process redirects one of these descriptors explicitely, a child process will inherit this redirection and read/write to a file when accessing cin/cout/cerr/clog WITHOUT NOTICING. Alternatives: Functions dup2_is_is for two istreams, dup2_os_os for two osstreams (both undocumented) -> behaviour and synopsis are the same. Parameters: In: i/ostream1: The stream to duplicate. This stream and it's undelying filedescriptor will stay unchanged. i/ostream2: The stream to be re-directed. The file associated to this stream will be closed, and this stream will be associated to the same file as i/ostream1. Examples: 1. myostream cout dup2 %subsequent writes to cout will be %redirected to myostream 2. myistream cin dup2 %subsequent reads from cin will read %from myinstream 3. % The "dup2" in 1. would better be preceeded by a "flush": mystream cout flush dup2 4. % A typical case: redirect output of a child process to a pipe. % (This is what the "spawn" command does): pipe %create a pipe { cout flush dup2 %The child redirects its cout to the %write end of the pipe closeistream %It closes the read end of the pipe, for %it is not needed. (ls) sysexec %The child transfers control to "ls". %"ls" will write to it's cout, i.e., to %the pipe. } spoon pop %we are not interested in the child's PID... closeostream %The parent closes the write end of the %pipe, for it is not needed. % The parent may now read the output of "ls" from the read end % of the pipe. Diagnostics: System Errors: EBADF (Invalid file descriptor) EINTR (Function was interrupted by a signal) Bugs: Author: R Kupper FirstVersion: May 05 1999 Remarks: This is a typesafe wrapper to "dup2_is_is" and "dup2_os_os" which operate on two input streams and two output streams respectively. The code for these functions can be found in file "synod2/sli/processes.{h|cc}". It sure is not a bad idea to flush ostream2 prior to the call, as it will be connected to another file afterwards, but any buffered data stays unchanged! Getting the file descriptors from C++-filestreams is not yet supported by a POSIX call. (There is such a call for C-filestreams, however). The code used here may be implementation dependent and not fully portable! As soon as there is a reliably portable way to detect file descriptors from a C++-filestream (or convert a C++ stream to a C stream), the respective code found in function Processes::fd(), file "synod2/sli/processes.cc", should be changed. SeeAlso: pipe, mkfifo */ /dup2 trie [/istreamtype /istreamtype] /dup2_is_is load addtotrie [/ostreamtype /ostreamtype] /dup2_os_os load addtotrie [/istreamtype /ostreamtype] /dup2_is_os load addtotrie [/ostreamtype /istreamtype] /dup2_os_is load addtotrie def %---------------------------------------------------------------------------- /* BeginDocumentation Name: spawn - Spawn a UNIX process and redirect stdin and stdout. Synopsis: CommandString spawn } { Flag=0: Status NormalExitFlag CommandArray spawn } { } { Flag=1 or omitted: CommandString spawn } -> { ear(write) mouth(read) CommandArray spawn } { } { Flag=2: CommandString Flag spawn } { ChildPID ear(write) mouth(read) CommandArray Flag spawn } Description: "spawn" does exectly what "system" does (see there). If Flag is 1 or 2, in addition, it redirects the spawned processes standard input and output to pipes that are then accesssible from the parent process. If you are interested in monitoring the information flow across the pipes, see "shpawn". Alternatives: Functions spawn_as_i with array or string followed bey an integer (flag), spawn_as_di_i with array or string and dictionary followed by an integer (both undocumented) -> behaviour and synopsis are the same. Parameters: In : CommandString(string), CommandArray(array of string): See "system" Flag(0|1|2) (Default=1): The Flag parameter is passed to "system": 0: "spawn" behaves absolut identical to "system" (see documentation). 1 or omitted: "system" is called in no-care-mode (see documentation). Pipes to standard input and output of the new Process are established. 2: "system" is called with Flag=2. (see documentation). Pipes to standard input and output of the new Process are established. The Child's PID is returned, for the user has to call "waitPID" expli- citely. Out: Status(integer), NormalExitFlag(boolean): See "system" ChildPID(integer): Process ID of the child process. Remember to call wait on this PID! ear(ostream): The write end of the pipe that is connected to the child's standard input. You can access the child's standard input by writing to "ear". mouth(istream): The read end of the pipe that is connected to the child's standard output. You can access the child's standard output by reading from "mouth". Examples: 1. %Spawn "ls" and read output from the pipe: (ls) spawn %spawn ls in no-care-mode exch closeostream %we do not need to write to ls! { eof {exit} {getline =} ifelse } loop %read and display data. closeistream %close connection 2. %Spawn "idl" and display output in SLI's callback cycle: (idl) spawn %spawn IDL in no-care-mode /IDL_mouth Set /IDL_ear Set %This procedure reads data from idl as long as it is available %and displays it on the screen: /IDL_talks { IDL_mouth { available {getline =} {exit} ifelse } loop pop } def %Hang this Procedure into callback cycle: /IDL_talks load setcallback %You may now issue IDL commands by sending them to IDL_ear %Output will be displayed on the screen: IDL_ear (help, !VERSION, /STRUCT) <- endl 3.%Instead of using SLI's callback cycle to display the output, %we could have used a parallel SLI process instead: { IDL_mouth {getline =} loop } spoon %In this case we even don not care about blocking... Diagnostics: -see section "Bugs"- Bugs: No error message is raised, if Flag=1 or Flag=2 and the given UNIX-command does not exist. To be correct, an error -is- raised, but it is raised in a child process of SLI, and -after- standard output has been redirected. The error message can be read from the "mouth"-pipe! "spawn" always returns sucessful if Flag=1 or Flag=2. However, excution of the UNIX command may have failed in the child process. This may be a bug or a feature, whatever you like best ;-> Author: R Kupper FirstVersion: May 06 1999 Remarks: "spawn" uses "system"'s Precode-Feature. (Caution - sophisticated programming style!!! ;->) For further remarks, see documentatin of "system". If you are interested in monitoring the information flow across the pipes, see "shpawn". To Do: In some cases, we will not need both the standard input and output redirected, but only one of them. In future versions, "spawn" may have the option to redirect only one of them. Respectively, there should by the option to redirect standard error as well. Possible calling sequence: command Flag stdinflag stdoutflag stderrflag spawn with stdinflag, stoutflag true as default, stderrflag false. It should be possible to spawn a parallel SLI Process. This should be supported by a future version. Actually, as this is the more general case, the spoon/system/spawn chain of commands could be restructured. SeeAlso: system, pipe, available, wait, environment */ % We will use the following code at two positions, so we store it in two % helper routines: /:spawn_Open_pipes_define_Precode_call_system {%stack: CommStr pipe %open up a pipe Parent>Child stack: CommStr >read_end >write_end pipe %open up a pipe Parent<Child stack: CommStr >read_end >write_end <read_end <write_end 5 -1 roll %stack: >read_end >write_end <read_end <write_end CommStr Flag %stack: >read_end >write_end <read_end <write_end CommStr Flag {%Precode for system: This will be executed immediately before the "sysexec": cout flush dup2 %The child redirects its cout to the write end of the <pipe closeistream %It closes the read end of the <pipe, for it is not needed. closeostream %It closes the write end of the >pipe, for it is not needed. cin dup2 %The child redirects its cin to the read end of the >pipe }%end of precode %stack: >read_end >write_end <read_end <write_end CommStr Flag Precode system %stack: >read_end >write_end <read_end <write_end results_of_system } bind def /:spawn_Open_pipes_define_Precode_call_system_withdict {%stack: CommStr EnvDict pipe %open up a pipe Parent>Child stack: CommStr EnvDict >read_end >write_end pipe %open up a pipe Parent<Child stack: CommStr EnvDict >read_end >write_end <read_end <write_end 6 -2 roll %stack: >read_end >write_end <read_end <write_end CommStr EnvDict Flag %stack: >read_end >write_end <read_end <write_end CommStr EnvDict Flag {%Precode for system: This will be executed immediately before the "sysexec": cout flush dup2 %The child redirects its cout to the write end of the <pipe closeistream %It closes the read end of the <pipe, for it is not needed. closeostream %It closes the write end of the >pipe, for it is not needed. cin dup2 %The child redirects its cin to the read end of the >pipe }%end of precode %stack: >read_end >write_end <read_end <write_end CommStr EnvDict Flag Precode system %stack: >read_end >write_end <read_end <write_end results_of_system } bind def /spawn_as_i %as: array or string { << >> begin % use local variables (for switch) %stack: CommStr Flag /Flag Set % Store flag in /Flag %stack: CommStr mark Flag 0 eq { Flag system %flag==0 - just call "system" end exit } case Flag 1 eq { %flag!=0 - open up pipes, define precode, call "system" :spawn_Open_pipes_define_Precode_call_system %call system with the precode. Flag==1, so it leaves no result. %stack: >read_end >write_end <read_end <write_end closeostream %Parent closes the write end of the <pipe, for it is not needed. %stack: >read_end >write_end <read_end 3 -1 roll %stack: >write_end <read_end >read_end closeistream %Parent closes the read end of the >pipe, for it is not needed. %stack : >write_end <read_end %that is: ear mouth end exit } case Flag 2 eq { %flag!=0 - open up pipes, define precode, call "system" :spawn_Open_pipes_define_Precode_call_system %call system with the precode. Flag==2, so PID is left as result. %stack: >read_end >write_end <read_end <write_end ChildPID 5 1 roll %stack: ChildPID >read_end >write_end <read_end <write_end closeostream %Parent closes the write end of the <pipe, for it is not needed. %stack: ChildPID >read_end >write_end <read_end 3 -1 roll %stack: ChildPID >write_end <read_end >read_end closeistream %Parent closes the read end of the >pipe, for it is not needed. %stack : ChildPID >write_end <read_end %that is: ChildPID ear mouth end exit } case {Flag end /spawn /FlagOutOfRange raiseerror} switchdefault } bind def %define trie structure: /spawn [/arraytype ] {1 spawn_as_i} def /spawn [/stringtype ] {1 spawn_as_i} def /spawn [/arraytype /integertype] /spawn_as_i load def /spawn [/stringtype /integertype] /spawn_as_i load def %---------------------------------------------------------------------------- /* BeginDocumentation Name: shpawn - Spawn a UNIX process using a shell and redirect stdin and stdout. Synopsis: CommandString shpawn } { Flag=0: Status NormalExitFlag CommandArray shpawn } { } { Flag=1 or omitted: CommandString Flag shpawn } -> { ear(write) mouth(read) CommandArray Flag shpawn } { { Flag=2: ChildPID ear(write) mouth(read) Description: "shpawn" does the same as "spawn" does (see there). However, the command is not executed directly but is passed to a shell which then will process the command. This way it is possible to use the full functionality of a shell, e.g. wildcard extension, alias, piping, ... By default, /bin/sh is used as the shell binary, and a login shell is requested by giving the -l flag. These values can be changed in the options dictionary, see sections "Options" below. In addition, the piping facility of the shell can be used to monitor the standard input and standard output of the invoked command. Monitoring can conveniently be turned on for stdin and stdout independently, via the options dictionary. See sections "Options" and "Remarks" below. Parameters: -see "spawn"- Options: Options-name: /shpawn Option type default description ---------------- ---------- -------- ------------------------------ /terminal string $TERM Terminal command to be used for monitoring stdin and stdout. /monitor-stdin booltype false True, if the standard input of the invoked command shall be monitored in a terminal window. /monitor-stout booltype false True, if the standard output of the invoked command shall be monitored in a terminal window. /sh-binary string (/bin/sh) Path to the shell binary to use. This binary must accept the flag -c for execution of a command. /sh-flags stringarray [] Flags added wehn calling the shell binary. Flags must be given as separate string elements (e.g. [(-l)]. The array may be empty. The flag -c is automatically added to this list of flags by the routine. Examples: %try these examples with "spawn" as well to see the difference: 1. (echo $PATH) 0 shpawn 2. (rm synod2/lib/help/cc/*.hlp) 0 shpawn 3. (ls | sort) 0 shpawn Diagnostics: -see "spawn"- Bugs: -see "spawn"- Author: R Kupper FirstVersion: May 19 1999 Remarks: "shpawn" invokes "/sh-binary" from the options dictionary (/bin/sh by default) and passes the given command after a "-c" flag. This means the specified shell binary must support this -c flag. The monitoring option for stdin and stdout exploits the piping deature of the shell, by piping information through instances of the "tee" command, which writes it to a file. (tee infile | command | tee outfile). These files are then monitored in separate subprocesses, using "tail" in a terminal window. The terminal command to be used is defined in the Options of shpawn. See manpages of sh, tee, tail, xterm/konsole for references. The monitoring option creates two files named /tmp/SLI-shpawn-monitor-stdin and /tmp/SLI-shpawn-monitor-stdout, which will persist after command execution terminated, so that their contents may be analyzed. SeeAlso: spawn, system, environment */ /shpawn << /terminal (TERM) getenv not {false} if /monitor-stdin false /monitor-stdout false /sh-binary (/bin/sh) /sh-flags [] >> Options %define a helper procedure: /:shpawn_comarray_to_string { %stack: comarray dup 0 get exch %stack: command comarray - Rem. Jun 21: Why that complicated? (Can't remember...) 0 1 erase %stack: command optionarray - Rem. Jun 21: Why not just turn the whole array into a string? {( ) exch join join} forall %stack: commandstring - Rem. Jun 21: leading or trailing blanks would -not- disturb the call! %stack: commandstring } bind def %define another helper routine: /:shpawn_add_monitors { %stack: (commandstring) << >> begin %local vars /commandstring Set commandstring %stack: (commandstring) /shpawn /monitor-stdin GetOption {%stack: (/tmp/SLI-shpawn-monitor-stdin) DeleteFile pop (touch /tmp/SLI-shpawn-monitor-stdin) system pop pop /shpawn /terminal GetOption ( -T ) join (STDIN: ) commandstring join ( ) (_) ReplaceOccurrences join %stack: (commandstring) (konsole -T 'stdin: commandstring') ( -e tail --follow /tmp/SLI-shpawn-monitor-stdin) join %stack: (commandstring) (konsole -T 'stdin: commandstring' -e tail --follow /tmp/SLI-shpawn-monitor-stdin) 1 system %stack: (commandstring) (tee /tmp/SLI-shpawn-monitor-stdin | ) exch join %stack: (tee /tmp/SLI-shpawn-monitor-stdin | commandstring) } if %stack: (commandstring) OR (prepended commandstring) /shpawn /monitor-stdout GetOption {%stack: (ev. prepended commandstring) (/tmp/SLI-shpawn-monitor-stdout) DeleteFile pop (touch /tmp/SLI-shpawn-monitor-stdout) system pop pop /shpawn /terminal GetOption ( -T ) join (STDOUT: ) commandstring join ( ) (_) ReplaceOccurrences join %stack: (ev. prepended commandstring) (konsole -T 'stdout: commandstring') ( -e tail --follow /tmp/SLI-shpawn-monitor-stdout) join %stack: (ev. prepended commandstring) (konsole -T 'stdout: commandstring' -e tail --follow /tmp/SLI-shpawn-monitor-stdout) 1 system %stack: (ev. prepended commandstring) (| tee /tmp/SLI-shpawn-monitor-stdout) join %stack: (ev. prepended commandstring | tee /tmp/SLI-shpawn-monitor-stdout) } if %stack: (commandstring) % OR %stack: (tee /tmp/SLI-shpawn-monitor-stdin | commandstring | tee /tmp/SLI-shpawn-monitor-stdout) % OR % ...etc. end %local vars } bind def /:shpawn_create_command_and_flags { /shpawn GetOptions begin sh-binary 1 arraystore sh-flags join (-c) append end } bind def %define trie structure: /shpawn [/arraytype] { %stack: comarray :shpawn_comarray_to_string %stack: commandstring :shpawn_add_monitors :shpawn_create_command_and_flags exch append %stack: [(/bin/sh) (-c) commandstring] 1 spawn_as_i } def /shpawn [/stringtype] { :shpawn_add_monitors :shpawn_create_command_and_flags exch append 1 spawn_as_i } def /shpawn [/arraytype /integertype] { exch %stack: flag comarray :shpawn_comarray_to_string %stack: flag commandstring :shpawn_add_monitors :shpawn_create_command_and_flags exch append %stack: flag [(/bin/sh) (-c) commandstring] exch spawn_as_i } def /shpawn [/stringtype /integertype] { exch :shpawn_add_monitors :shpawn_create_command_and_flags exch append exch spawn_as_i } def %END: define trie structure for shpawn. %---------------------------------------------------------------------------- /* BeginDocumentation Name: isatty - Determine if a stream is connected to a terminal. Synopsis: stream isatty -> false|true Description: Alternatives: Functions isatty_is for istreams, isatty_os for osstreams (both undocumented) -> behaviour and synopsis are the same. Remarks: This is a wrapper to the POSIX kernel function istty(). SeeAlso: ctermid */ /isatty [/istreamtype] /isatty_is load def /isatty [/ostreamtype] /isatty_os load def /* BeginDocumentation Name: taskset - Binds a process to a processor. Synopsis: proc pid taskset -> - Parameters: proc - This integer value identifies a certain processing unit of the system the current job is running on. On a system with multi-core processors each core is assigned a different integer. pid - This integer value identifies the process that should be bound to the procesing unit proc. Description: This function is a SLI interface to the UNIX command taskset. Author: Diesmann FirstVersion: September 9th, 2006 SeeAlso: taskset_thisjob, Rank, NumProcesses */ /taskset [/stringtype /integertype] { exch osstream pop (taskset -cp ) <- exch <- ( ) <- exch <- str system } def /* BeginDocumentation Name: taskset_thisjob - Binds the current process to a processor. Synopsis: proc taskset_thisjob -> - Parameters: proc - This integer value identifies a certain processing unit of the system the current job is running on. On a system with multi-core processors each core is assigned a different integer. Description: This function is a SLI interface to the UNIX command taskset. The command is particularly useful for assigning the different jobs of a distributed simulation to different processors or computing cores independent of the MPI implementation used. Binding of jobs is helpful in benchmarking to quantify the effects of hardware shared by computing cores and the influence of the job scheduling og the operating system. Examples: Consider a particular computer system with 4 processors with 2 computing cores each. The operating system assigns the identifiers 0 to 7 to the 8 cores starting with 0 for the first core on the first processor, 1 for the second core on the first processor, followed by 2 for the first core on the second processor, and so on. The task now is to make sure that a distributed simulation with 4 MPI jobs uses only a single core on each processor. This can be achieved by inserting the following command as the first line of the simulation script: [1 3 5 7] Rank get taskset_thisjob or alternatively, Rank 2 mul 1 add taskset_thisjob In this examples only odd core identifiers are used because it is assumed that the operating system has a tendency to interrogate core 0 for availability. Following this strategy more general schemes can be designed to minimize the use of shared hardware on the basis of the architecture of a given computer system. Author: Diesmann FirstVersion: September 9th, 2006 SeeAlso: taskset, Rank, NumProcesses */ /taskset_thisjob [/stringtype] { getPID taskset } def % cat /proc/<pid>/status | grep VmSize %/memory_thisjob %{ % (cat /proc/) getPID cvs join (/status | grep VmSize) join shpawn %spawn ls in no-care-mode % % exch closeostream %we do not need to write to ls! % { eof {exit} {getline {exch} if} ifelse } loop %read and display data. % closeistream %close connection % % 0 7 erase % size 3 sub 3 erase % assuming kb may be unsafe % cvi %} def /* BeginDocumentation Name: memory_thisjob - report virtual memory size for current NEST process Synopsis: memory_thisjob -> vmsize Description: This function returns the virtual memory size (VmSize) for the current NEST process by checking the /proc filesystem. VmSize is reported in kB. Note: This function is currently available under Linux and OSX. Under OSX, it requires procfs which is available from http://osxbook.com/book/bonus/chapter11/procfs or via MacPorts. procfs must be mounted at /proc. */ /memory_thisjob { /os statusdict /hostos get def mark os (darwin) searchif { memory_thisjob_darwin exit } case os (linux) searchif { memory_thisjob_linux exit } case true { (I do not know how to check memory under ) os join M_ERROR message /memory_thisjob /UnknowOS raiseerror exit } case switch } def /memory_thisjob_linux { (/proc/) getPID cvs join (/status) join ifstream not { (No information available on the NEST process from the /proc filesystem. ) M_ERROR message /memory_thisjob_linux /ProcOpenFailed raiseerror } if { eof {exit} {getline {dup (VmSize:) searchif {exch exit} {pop} ifelse } if} ifelse } loop closeistream %close connection 0 7 erase size 3 sub 3 erase % assuming kb may be unsafe cvi } def /memory_thisjob_darwin { % OSX version, requires procfs from, e.g., MacPorts (/proc/) getPID cvs join (/task/basic_info/virtual_size) join ifstream { % opened successfully; file has just one line: nnnnnnn KB gets cvi % read just number and convert to num exch closeistream } { (No information available on the NEST process from the /proc filesystem. ) (Under OSX, you must have procfs mounted at /proc. procfs is available ) join (from http://osxbook.com/book/bonus/chapter11/procfs or via MacPorts.) join M_ERROR message /memory_thisjob_darwin /ProcOpenFailed raiseerror } ifelse } def /memory_thisjob_bluegenep { % Blue Gene/P version, requires the function BGPMemInfo from NestModule memory_thisjob_bgp /mem Set mem /heap get mem /stack get add } def /hardware_memory_thisjob { (/proc/) getPID cvs join (/status) join ifstream not { (No information available on the NEST process from the /proc filesystem. ) M_ERROR message /hardware_memory_thisjob /ProcOpenFailed raiseerror } if { eof {exit} {getline {dup (VmHWM:) searchif {exch exit} {pop} ifelse } if} ifelse } loop closeistream %close connection 0 6 erase size 3 sub 3 erase % assuming kb may be unsafe cvi } def /meminfo_thisjob { (/proc/) getPID cvs join (/statm) join ifstream not { (No information available on the NEST process from the /proc filesystem. ) M_ERROR message /meminfo_thisjob /ProcOpenFailed raiseerror } if getline not { (No information available on the NEST process from the /proc filesystem. ) M_ERROR message /meminfo_thisjob /ProcOpenFailed raiseerror } if exch closeistream %close connection } def % experimental, MD 090413 statusdict/exitcodes :: /restart_serial 4 put % experimental, MD 090413 /mpirun_call { /mpirun lookup % better mpirun_name {pop} { M_FATAL (mpirun_call) (Cannot find definition of SLI command /mpirun.) message M_FATAL (mpirun_call) (Please consult ~/.nestrc to learn how to provide) message M_FATAL (mpirun_call) (NEST with this command.) message } ifelse mpirun 0 shpawn % arguments of mpirun are identical to the ones of mpirun_call pop % don't know what to do with the boolean returnd by shpawn % return value is return value of command } def /* BeginDocumentation Name: nest_serial - Returns a non-distributed call of nest as a string Synopsis: (sli-filename) nest_serial -> (string with shell command) Description: If nest was compiled with MPI it may be unsafe to directly call the binary to obtain a non-distributed version. The command nest_serial looks up the appropriate way to call nest and returns a suitable command as a string. The algorithm is as follows: 1. if nest is not compiled with MPI, return "nest" 2. if sli command /mpirun is defined, return this with the -np 1 equivalent 3. issue and error with the message that nest was compiled with MPI but no method to call nest has been specified. Diagnostics: This function issues an error if NEST was compiled with MPI but is not aware of a safe method to call a non-distributed version of nest. Examples: (hello.sli) nest_serial -> (mpirun -mca btl self,tcp -np 1 /home/dummy/nest/bin/nest hello.sli) Author: Diesmann FirstVersion: 090711 SeeAlso: nest_indirect */ /nest_serial [/stringtype] { statusdict/have_mpi :: { /mpirun lookup { pop 1 exch mpirun } { M_FATAL (nest_serial) (Command nest may fail because NEST was compiled with ) (MPI but ~/.nestrc does not specify a corresponding method ) join (to call nest. This is required even if you intend to run ) join (a serial job although with some MPI implementations you can ) join (execute the nest binary directly. Please consult ~/.nestrc ) join (to learn how to provide NEST with the command /mpirun.) join message statusdict/exitcodes/fatal :: quit_i } ifelse } { statusdict /prefix get (/bin/nest ) join exch join } ifelse } def /* BeginDocumentation Name: nest_indirect - Returns a nest call as a string able to spawn distributed simulations Synopsis: (sli-filename) nest_indirect -> (string with shell command) Description: If a task requires multiple distributed simulations with different numbers of processors NEST needs to be compiled with support for distributed computing and be aware of the corresponding method to start itself. However, some libraries for distributed computing do not support the nested call of further processes. Therefore, nest_indirect provides a serial environment for the preparation of the distributed simulations. The algorithm is as follows: 1. if nest is not compiled with MPI, quit with fatal error 2. if sli command /mpirun is not defined, quit with fatal error 3. return "sli" Diagnostics: This function quits nest with a fatal error if there is no support for distributed computing or NEST does not know how to start a distributed version of itself. Examples: (hello.sli) nest_indirect -> (/home/dummy/nest/bin/sli hello.sli) Author: Diesmann FirstVersion: 090711 SeeAlso: nest_serial */ /nest_indirect [/stringtype] { statusdict/have_mpi :: not { M_FATAL (nest_indirect) (The nest binary has no support for distributed computing.) message M_FATAL (nest_indirect) (Please recompile with e.g. MPI.) message statusdict/exitcodes/fatal :: quit_i } if /mpirun lookup { pop statusdict /prefix get (/bin/sli ) join exch join } { M_FATAL (nest_indirect) (NEST was compiled with MPI but ~/.nestrc does not specify) message M_FATAL (nest_indirect) (a corresponding method to call nest. Therefore NEST does) message M_FATAL (nest_indirect) (not know how to start itself in a distributed manner.) message M_FATAL (nest_indirect) (Please consult ~/.nestrc to learn how to provide NEST with) message M_FATAL (nest_indirect) (the command /mpirun.) message statusdict/exitcodes/fatal :: quit_i } ifelse } def %---------------------------------------------------------------------------- /* Begin(LOESCHMISCH!)Documentation Name: Synopsis: Description: Parameters: In : Out: Examples: Diagnostics: Bugs: Author: FirstVersion: Remarks: References: Variants: SeeAlso: */