At Sun, 14 Apr 2019 01:52:07 -0700 (PDT) Alexandru <
alexandr...@meshparts.de> wrote:
>
> Am Sonntag, 14. April 2019 05:49:49 UTC+2 schrieb Robert Heller:
> > At Sat, 13 Apr 2019 18:48:46 -0700 (PDT) Alexandru <alexandru.dadalau@mes=
>
hparts.de> wrote:
> >=20
> > >=20
> > > Am Sonntag, 14. April 2019 03:39:01 UTC+2 schrieb Robert Heller:
> > > > At Sat, 13 Apr 2019 18:12:22 -0700 (PDT) Alexandru <alexandru.dadalau=
> @
meshparts.de> wrote:
> > > >=20
> > > > >=20
> > > > > I managed to solve the problem. Though I still don't understand the=
> true reason, I'll try to explain what I know.
> > > > >=20
> > > > > After executing the external process with "exec ... &" I had a call=
> to a waiting procedure, that waited for the process to finish:
> > > > >=20
> > > > > proc ProcessWaitToFinish {pid {cycle 100}} {
> > > > > if {[::twapi::process_exists $pid]} {
> > > > > after $cycle [list ProcessWaitToFinish $pid $cycle]
> > > > > }
> > > > > }
> > > > >=20
> > > > > set pid [exec gmsh.exe &]
> > > > > ProcessWaitToFinish $pid
> > > > >=20
> > > > > The above constellation did not worked and I realized it was becaus=
> e ProcessWaitToFinish did not blocked the further execution of the program.=
> What I don't understand, is why I could not see this.
> > > > >=20
> > > > > The corrected version looks like this:
> > > > >=20
> > > > > proc ProcessWaitToFinish {pid {cycle 100}} {
> > > > > if {[::twapi::process_exists $pid]} {
> > > > > after $cycle [list ProcessWaitToFinish $pid $cycle]
> > > > > } else {
> > > > > uplevel 1 {set pid ""}
> > > > > }
> > > > > }
> > > > > =20
> > > > > set pid [exec gmsh.exe &]
> > > > > ProcessWaitToFinish $pid
> > > > > vwait pid
> > > >=20
> > > > The "after" command needs the event loop running to work properly (in=
> the form
> > > > you are using -- "after time script"). Normally plain tclsh *does not=
> run the
> > > > event loop* -- everything runs synchronously. Wish *always* runs the =
> event loop
> > > > (all of the GUI code depends on the event loop -- everything is async=
> hronously
> > > > event driven).
> > > >=20
> > > > When you run a plain CLI tclsh program that needs the event loop, you=
> need to=20
> > > > enter the event loop. The way you do this is by using the vwait comm=
> and,=20
> > > > which changes the program from a synchronous program to an asynchrono=
> us one. =20
> > > > Which you discovered.
> > > >=20
> > > > > =
> =20
> > > >=20
> > > > --=20
> > >=20
> > > Thought so too from the beginning. The app is alrady running in wish an=
> d I
> > > explicitly enter the event loop by "vwait forever" at the end of the co=
> de.
> > > But obviously that was not enough.=20
> >=20
> > If the application is running from wish, it is already running the event =
> loop.=20
> >=20
> > I don't understand what *exactly* you want to do.
> >=20
> > This code:
> >=20
> > proc ProcessWaitToFinish {pid {cycle 100}} {
> > if {[::twapi::process_exists $pid]} {
> > after $cycle [list ProcessWaitToFinish $pid $cycle]
> > } else {
> > uplevel 1 {set pid ""}
> > }
> > }
> > =20
> > set pid [exec gmsh.exe &]
> > ProcessWaitToFinish $pid
> > vwait pid
> >=20
> > *Effectivly* does the same as:
> >=20
> > exec gmsh.exe
> >=20
> > *Except* the event loop is running, allowing events to be processed.
> >=20
> > It *sounds* like you want to run a program, *wait for it to finish*, but =
> allow=20
> > events to be serviced in meantime. Is this correct? It is not really ab=
> out=20
> > blocking/nonblocking.
> >=20
> > If so, what you really want to be doing is this:
> >=20
> > proc ProcessWaitToFinish {pidname {cycle 100}} {
> > upvar $pidname pid
> > if {[::twapi::process_exists $pid]} {
> > after $cycle [list ProcessWaitToFinish pid $cycle]
> > } else {
> > set pid 0
> > }
> > }
> >=20
> > set pid [exec gmsh.exe &]
> > ProcessWaitToFinish pid
> > vwait pid
>
> While I find your method more elegant than mine because it's using upvar in=
> stead of uplevel and it's more general, I think there is no big difference =
> and the result will be the same.
*Except* your version depends on a hardwired global variable name. It will
*break* if you, for example do this:
set otherpid [exec foo.exe &]
ProcessWaitToFinish $otherpid
vwait otherpid
>
> >=20
> >=20
> > Actually, a better way would be to do things the OO way using snit (this=
> =20
> > avoids hardcoded global variables):
> >=20
> > package require snit
> >=20
> >=20
> > snit::stringtype ExecutableFile -regexp {^.+\.exe$}
> > snit::type runProcess {
> > variable pid 0
> > option -executable -readonly yes -type ExecutableFile
> > constructor {args} {
> > $self configurelist $args
> > }
> > method run {} {
> > set pid [exec $options(-executable) &]
> > $self _waitToFinish
> > vwait [myvar pid]
> > }
> > method _waitToFinish {{cycle 100}} {
> > if {[::twapi::process_exists $pid]} {
> > after $cycle [mymethod _waitToFinish $cycle]
> > } else {
> > set pid 0
> > }
> > }
> > }
> >=20
> > Then:
> >=20
> > runProcess create gmsh -executable gmsh.exe
>
> That's exactly what I ultimately want to achieve (see OP): Run multiple exe=
> cutables in parallel and wait for them to finish, then send new ones as soo=
> n they finish. I could do this with thread but I was just wondering if even=
> t based programming will also work. Must I use snit? I immagined my origina=
> l proc should also work when multiple execution run in parallel(?)
Actually it will break, since you will have hardwired the global variable name
(pid). *My* first version passes the name of the variable that is being
waited on, and my SNIT version uses a class instance variable.
If you want to run multiple processes in parallel, you will need to separate
the waits from the exe starts:
package require snit
snit::stringtype ExecutableFile -regexp {^.+\.exe$}
snit::type runProcess {
variable pid 0
option -executable -readonly yes -type ExecutableFile
constructor {args} {
$self configurelist $args
}
method run {} {
set pid [exec $options(-executable) &]
}
method wait {} {
$self _waitToFinish
vwait [myvar pid]
}
method _waitToFinish {{cycle 100}} {
if {[::twapi::process_exists $pid]} {
after $cycle [mymethod _waitToFinish $cycle]
} else {
set pid 0
}
}
}
Then:
# Start gmsh.exe
runProcess create gmsh -executable gmsh.exe
gmsh run
# Start other.exe
runProcess create other -executable other.exe
other run
# Start another.exe
runProcess create another -executable another.exe
run another
# Wait for gmsh.exe to finish (other.exe and another.exe will also be running)
# while we are waiting for it to complete, Tk events are being handled.
gmsh wait
# gmsh.exe finished, now wait for other.exe to finish (it might already be done)
other wait
# other.exe is done, now wait for another.exe
another wait
# all processes are now done.
Note with this code, one can restart the processes later, should you need to.
>
> >=20
> > gmsh run
> >=20
> > Note: coded this way, you can run the program as many times as you need.q
> > --=20