#!/usr/bin/env tclsh # Launches many processes and wait for them to finish. # This script will works on systems that has the ps command such as # BSD, Linux, and OS X package require Tclx; # For process-management utilities package require struct::set proc updatePidList {stat} { global pidList global allFinished # Parse the process ID of the just-finished process lassign $stat processId howProcessEnded exitCode # Remove this process ID from the list of process IDs #set pidList [lindex [intersect3 $pidList $processId] 0] struct::set exclude pidList $processId set processCount [llength $pidList] # Occasionally, a child process quits but the signal was lost. This # block of code will go through the list of remaining process IDs # and remove those that has finished set updatedPidList {} foreach pid $pidList { if {![catch {exec ps $pid} errmsg]} { lappend updatedPidList $pid } } set pidList $updatedPidList # Show the remaining processes if {$processCount > 0} { puts "Waiting for [struct::set size $pidList] processes" } else { set allFinished 1 puts "All finished" } } # A signal handler that gets called when a child process finished. # This handler needs to exit quickly, so it delegates the real works to # the proc updatePidList proc childTerminated {} { # Restart the handler signal -restart trap SIGCHLD childTerminated # Update the list of process IDs while {![catch {wait -nohang} stat] && $stat ne {}} { after idle [list updatePidList $stat] } } # # Main starts here # puts "Main begins" set pidList {} set allFinished 0 # When a child process exits, call proc childTerminated signal -restart trap SIGCHLD childTerminated # Spawn 3 processes set childId [exec tclsh child.tcl 1 7 &]; # #1 runs for 7 secs struct::set include pidList $childId set childId [exec tclsh child.tcl 2 5 &]; # #2 runs for 5 secs struct::set include pidList $childId set childId [exec tclsh child.tcl 3 20 &]; #3 runs for 20 secs struct::set include pidList $childId # Do some processing puts "list of processes: $pidList" puts "Waiting for child processes to finish" # Do some more processing if required # After all done, wait for all to finish before exiting # Will time out in 2 minutes after 120000 { set allFinished "timedout" } vwait allFinished if {$allFinished == "timedout"} { puts "Timed out" } puts "Main ends"