Ephaeton at gmx dot net a person who's been away from Tcl for too long...Wiki stuff:
- Errx
- AtExit Handlers
- Some socket examples in Einfach Tcl
- pathentry
- block-select
- sizepanel
- multiple value set
- likes Toasting procs
- Simpletracer, with help from RS & MS
- onInit
- sizepanel
- Now a fossil contributor
This will be moved to another page once I find a good name for it.
msw suchenwi: I managed to shape up the script which is giving me vwait problems* suchenwi can't look - busy with paywork .}msw have a look at wiki://MSW, there's code and output from a run showing what I'm getting mad about :)msw ...once you have time that is.msw with tclsh running you just don't get the code to fuck up ... with tk it's piece of cakemsw Suppose you'd need to tokenize each run with own target variable etc. so it doesn't try to be multiply in the same procmsw you others are well invited to have a look and tell me what I did wrong, too :)arjen I am sorry: ight now I have my own things to get mad about ;)msw oh, there's no pressure here, I've found a(n uberugly) workaround for my problemmsw Just for the usual cursing/"what's NOT possible"/things to avoid - notebook..suchenwi msw: maybe you should just disable thew button when clicked, and re-enable it when bexec is done...msw suchenwi: that's what I'm doing (a bit different, using a grab on a label saying have patience)suchenwi As a single global variable bexec_result holds the results, two "parallel" bexecs are only guaranteed to make trouble.msw suchenwi: but it's definitely a (n ugly) workaroundmsw suchenwi: that's why I'm lockingmsw suchenwi: and the locking is exposing the actual problem..msw suchenwi: after the command is done, and the vwait returns, the proc is twice at the same position - before the acquire-lock partsuchenwi Maybe just to use ::bexec_runs to signal whether bexec is ain progress: if $::bexec_runs returnmsw suchenwi: version #1 was doing that kind of ...suchenwi Initializi to 0, set to 1 in the first bexec call, reset to 0 after the vwait.msw if {$bexec_runs} then {vwait bexec_runs} ..msw I should maybe add after idle to the vwaitsuchenwi You can't vwait in two places for the same variable.msw that's the dilemma :)suchenwi But you can check its value and return early.msw so I need to tokenize if I want to have the stuff be able to run in parallelmsw like vwait [token-var], append result to [token-var] etc.suchenwi Yes - sort of like the http package does it.msw my manpage doesn't mention that you cannot vwait on the same var twice btw.msw (8.3)suchenwi Hm.. maybe you can, but it seems conceptually unclear to me - which vwait fires first, etc.* suchenwi meetingmsw basically it should be fifomsw but well, some core-savvy wants to comment ? :)msw 8.4 vwait man doesn't mention it eithernem vwaits stack - I would assume that it would be a filo - last vwait would fire before firstmsw well, event _queue_, would've supposed an append each time, but that doesn't matter ...msw I wouldn't care about the order ...msw if the events would be ok, but it seems broken (check the output on wiki.tcl.tk/msw, bottom of page)* dkf backsuchenwi Donal: can two vwaits wait for the same variable?dkf Yessuchenwi And in what order are they released?dkf Strict stack ordersuchenwi But with "parallel" events, is there a stack order?dkf There's always a stack orderdkf 'Cos we're all implemented in C...suchenwi Ah, right :)dkf The problem with msw's code is that it is trying to use stack-based operation with event handlingmsw I've the feeling all I need is a closure and it'll work :pdkf This is the sort of thing that http://wiki.tcl.tk/1255 warns againstdkf Actually, you need continuations ;)msw ok, gimme :)dkf But in their absence, what you need to do is to restructure your code so that instead of bexec returning the result, it takes a script which it hands off the result to when the background execution is done.dkf With that done, you can rewrite your code to be completely free of calls to vwait and have it work.msw ok, what I called "tokenize".msw er wait, I need one vwait at least ... (should work in tcl, too)dkf If it is to work in standard Tcl, then you add a [vwait forever] in your main scriptmsw hmm but it's used like ..msw < synchronous > ... use "bgexec" < .. more synchronous >suchenwi postprocess [bexec $input]msw ah ok, when I have to pass a script anyways ..msw grmpf, mental recursion coming up.msw it's actually <synch> (bgexec + synch) * n <synch> .. the * n is going to be fun.msw pity I need both stdout and stderr, split and "live" while the command runs... minus that requirement, would be dead easy..* msw figures that if it was obvious, wouldn't need CODE to realise.dkf Sorry it took so long to write, but there was quite a bit to read ;)dkf s/read/writenem is the if {[gets $f line] == -1} { .. ok? I usually use if {[catch {gets $f} line] || [eof $f]} { ...nem I seem to remember being taught to do that a long long time agodkf the gets line really is okdkf Since we're not blockingnem Ah, okdkf And eof results in -1 resultmsw was just thinking the same, but the old version used read first then checked with eofdkf Technically, it's the close that could faildkf The gets version should work as wellnem Thinking about it the catch/eof combo might possibly fail to read the last linedkf And I'm singularly unworried about malicious code; we're running a general command here after all ;)dkf nem: Do you do incomplete last lines in your files?nem not usually - but it's a possibilitydkf Notice the total lack of vwait in that codenem vwait is eeeevil.msw yeah, I noticed.msw if it's eeeevil, tcl should kick it out :)dkf vwait is only evil if you have reentrant code, but reentrant vwait is a real problemdkf Sometimes you need itdkf (e.g. modal dialog boxes)nem modal dialog boxes are eeevildkf Won't argue with that ;)nem tk_messageBox usage of vwait internally has bitten me several times (to the point where I don't use it any more)dkf But sometimes the client wants their soul sold to the devil anywaydkf It's OK, but you have to take care to stop *two* simultaneous runs of the same codemsw vwait should be expanded so that this works too :)
MSW (broken) version
set ::bexec_runs 0 set ::bexec_result "" set ::bnum 0 proc bexec what { global bexec_runs bexec_result bnum incr bnum set fname [file join $::env(HOME) tmp cmd_run_lock_$::env(USER)_[pid]] while {[catch {set fp [open $fname {WRONLY CREAT EXCL}]}]} { puts stderr "($bnum) Lock busy, will retry." after 200 "set ::retry 0" vwait ::retry } puts stderr "($bnum)Got the Lock for \"[string range $what 0 99]...\"." # reset results set ::bexec_result "" puts stderr "Executing $what..." set bexec_runs 1 set f [open |$what] #fconfigure $f -translation none fileevent $f readable [list bexec_read $f] vwait bexec_runs file delete -force [file join $::env(HOME) tmp cmd_run_lock_$::env(USER)_[pid]] puts stderr "($bnum)Released the Lock for \"[string range $what 0 99]...\"." return $bexec_result } proc bexec_read {f} { set data [read $f] if [eof $f] { close $f set ::bexec_runs 0 } append ::bexec_result $data } # demo1: set tfile [open tst.sh w] puts $tfile {#!/bin/sh echo "output1" sleep 3 echo "output2"} close $tfile puts [bexec "sh tst.sh"] puts [bexec "sh tst.sh"] ;# <-- this one will wait. # demo2: pack [button .b -text "run" -command {puts [bexec "sh tst.sh"]}] -expand 1 -fill bothOutput:
(1)Got the Lock for "sh tst.sh...". (1)Executing sh tst.sh... (1)Released the Lock for "sh tst.sh...". output1 output2 (2)Got the Lock for "sh tst.sh...". (2)Executing sh tst.sh... (2)Released the Lock for "sh tst.sh...". output1 output2 (3)Got the Lock for "sh tst.sh...". (3)Executing sh tst.sh... (3)Released the Lock for "sh tst.sh...". output1 output2 (4)Got the Lock for "sh tst.sh...". (4)Executing sh tst.sh... (5) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. (6) Lock busy, will retry. # ad infitum... lockfile never goes away.DKF VERSION
set ::bexec_runs 0 set ::bexec_result "" set ::bnum 0 proc bexec {what handler {uid -1}} { global bexec_accum bnum if {$uid == -1} { set uid [incr bnum] } set fname [file join ~ tmp cmd_run_lock_[pid]] if {[catch {open $fname {WRONLY CREAT EXCL}} fp]} { puts stderr "($uid) Lock busy, will retry." after 200 [list bexec $what $handler $uid] return } set f [open |$what] set bexec_accum($uid) {} fileevent $f readable [list bexec_read $f $uid $fname $handler] } proc bexec_read {f uid fname handler} { global bexec_accum if {[gets $f line] == -1} { close $f file delete -force $fname uplevel #0 $handler [list $bexec_accum($uid)] unset bexec_accum($uid) return } append bexec_accum($uid) $line \n } # demo1: set tfile [open tst.sh w] puts $tfile {#!/bin/sh echo "output1" sleep 3 echo "output2"} close $tfile bexec "sh tst.sh" puts bexec "sh tst.sh" puts ;# <-- this one will wait. # demo2: pack [button .b -text "run" -command {bexec "sh tst.sh" puts}] -expand 1 -fill bothOutput:
(2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. (2) Lock busy, will retry. output1 output2 output1 output2 (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. (4) Lock busy, will retry. output1 output2 (5) Lock busy, will retry. (5) Lock busy, will retry. (5) Lock busy, will retry. output1 output2 output1 output2