Posted to tcl by sebres at Wed Dec 05 16:47:49 GMT 2018view raw

  1. ## ================================================================================
  2. ## Following code shows the unexpected behavior: no EOF on pipe of the child process,
  3. ## if still one child started from.
  4. ## PoC:
  5. ## Initial process starts a child process asynchronously and waits for end of file,
  6. ## the result of the script execution depends on first parameter (WITH_DELAY_SUBPROC):
  7. ## 0. This child is alone and does not start another child, then EOF is signaled
  8. ## in the main process.
  9. ## 1. This child started still one process, then NO EOF signaled
  10. ## in the main process (one see a timeout after 2 seconds).
  11. ##
  12. ## Reproducible for unix/windows for all tcl-versions > 8.5
  13. ##
  14. ## Conclusion:
  15. ## The handle to pipe seems to be inherited from sub-child.
  16. ## ================================================================================
  17.  
  18. lassign $::argv WITH_DELAY_SUBPROC WITH_TIMEOUT WITH_TICKS
  19. if {$WITH_DELAY_SUBPROC eq ""} {set WITH_DELAY_SUBPROC 1}
  20. if {$WITH_TIMEOUT eq ""} {set WITH_TIMEOUT 1}
  21. if {$WITH_TICKS eq ""} {set WITH_TICKS 0}
  22.  
  23. # Scripts:
  24. set script [file join [if [info exists env(TEMP)] {set env(TEMP)} {set _ /tmp}] test-script-sock-12.2.tcl]
  25. set script1 [file root $script]--2.tcl
  26. if {$WITH_DELAY_SUBPROC} {
  27. set f [open $script1 w]
  28. puts $f {
  29. after 5000 exit
  30. vwait forever
  31. }
  32. close $f
  33. }
  34.  
  35. set f [open $script w]
  36. puts $f {puts "testing start"}
  37. if {$WITH_DELAY_SUBPROC} {
  38. puts $f {puts " sub-process ..."}
  39. puts $f [list exec [info nameofexecutable] $script1 &]
  40. }
  41. puts $f {
  42. puts "testing end, exit"
  43. exit
  44. }
  45. close $f
  46.  
  47. # Logger:
  48. variable start [clock milliseconds]
  49. proc log {s} {
  50. variable start
  51. puts "+[format %04d [expr {[clock milliseconds] - $start}]] $s"
  52. }
  53.  
  54. # Readable event from script, "waiting" for EOF (pipe closed):
  55. proc readfromscript {p} {
  56. # wait for end of p:
  57. variable done
  58. log "=== read, done:$done, gets:[gets $p], eof:[eof $p] ==="
  59. if {![eof $p]} return
  60. fileevent $p readable {}
  61. # Pipe closed - process exited, give few time (0.1s) to fulfill the end.
  62. variable x
  63. set x "process ended"
  64. log "======== EOF -> AFTER ======="
  65. after 100 [list set done 1]
  66. #close $p
  67. }
  68.  
  69. set done 0
  70.  
  71. log start
  72. set p [open "|[list [info nameofexecutable] $script]" w+]
  73. fconfigure $p -buffering none -blocking 0
  74. fileevent $p readable [list readfromscript $p]
  75.  
  76. set x none
  77. if {$WITH_TIMEOUT} {
  78. set tmr [after 2000 {set x timeout; set done -1}]
  79. }
  80. if {$WITH_TICKS} {
  81. proc ticker {} {
  82. variable done
  83. variable p
  84. log "... tick: done:$done, eof:[eof $p]"
  85. after 50 ticker
  86. }
  87. ticker
  88. }
  89. vwait done
  90. log "done:$done, x:$x"
  91. if {$WITH_TIMEOUT} {
  92. after cancel $tmr
  93. }
  94. catch {close $p}
  95. log end.
  96.  
  97. file delete $script
  98. file delete $script1
  99.  
  100. return
  101.  
  102. ## Results:
  103.  
  104. ## without sub-process (works as expected):
  105. $ tclsh ~/test.tcl 0
  106. +0000 start
  107. +0002 === read, done:0, gets:testing start, eof:0 ===
  108. +0002 === read, done:0, gets:testing end, exit, eof:0 ===
  109. +0002 === read, done:0, gets:, eof:1 ===
  110. +0002 ======== EOF -> AFTER =======
  111. +0102 done:1, x:process ended
  112. +0102 end.
  113.  
  114. ## with sub-process (no EOF on first process, the handle to pipe seems to be inherited from sub-child):
  115. $ tclsh ~/test.tcl 1
  116. +0000 start
  117. +0002 === read, done:0, gets:testing start, eof:0 ===
  118. +0002 === read, done:0, gets: sub-process ..., eof:0 ===
  119. +0002 === read, done:0, gets:testing end, exit, eof:0 ===
  120. +2000 done:-1, x:timeout
  121. +2000 end.
  122.  

Comments

Posted by sebres at Wed Dec 05 17:06:24 GMT 2018 [text] [code]

if timeout disabled (WITH_TIMEOUT = 0), it will also signal EOF on pipe in main-process, but firstly when the child sub-process ends (so after 5 seconds): $ tclsh ~/test.tcl 1 0 +0000 start +0002 === read, done:0, gets:testing start, eof:0 === +0002 === read, done:0, gets: sub-process ..., eof:0 === +0002 === read, done:0, gets:testing end, exit, eof:0 === +5005 === read, done:0, gets:, eof:1 === +5005 ======== EOF -> AFTER ======= +5105 done:1, x:process ended +5105 end.