diff options
author | apnadkarni <apnmbx-wits@yahoo.com> | 2022-07-05 15:54:00 (GMT) |
---|---|---|
committer | apnadkarni <apnmbx-wits@yahoo.com> | 2022-07-05 15:54:00 (GMT) |
commit | eb33dbb186b524be57dd9b4c2ebb9b703cb5080a (patch) | |
tree | becd3f8375a439498e9dfc6f96a91d1c10042b08 | |
parent | dda0e7f5f62db678465c31383298f07ab102a2d1 (diff) | |
download | tcl-eb33dbb186b524be57dd9b4c2ebb9b703cb5080a.zip tcl-eb33dbb186b524be57dd9b4c2ebb9b703cb5080a.tar.gz tcl-eb33dbb186b524be57dd9b4c2ebb9b703cb5080a.tar.bz2 |
Fix channel close on thread exit if other threads exist. Add winconsole tests.
-rw-r--r-- | tests/winConsole.test | 237 | ||||
-rw-r--r-- | win/tclWinConsole.c | 33 |
2 files changed, 231 insertions, 39 deletions
diff --git a/tests/winConsole.test b/tests/winConsole.test index 6d537b2..cb1babc 100644 --- a/tests/winConsole.test +++ b/tests/winConsole.test @@ -14,7 +14,9 @@ if {"::tcltest" ni [namespace children]} { namespace import -force ::tcltest::* } -::tcltest::ConstraintInitializer twapi { expr {![catch {package require twapi}]} } +catch {package require twapi} ;# Only to bring window to foreground. Not critical + +::tcltest::ConstraintInitializer haveThread { expr {![catch {package require Thread}]} } # Prompt user for a yes/no response proc yesno {question {default "Y"}} { @@ -44,14 +46,16 @@ proc prompt {prompt} { # Input tests test console-gets-1.0 {Console blocking gets} -constraints {win interactive} -body { - set response [prompt "Type \"xyz\" and hit Enter: "] + prompt "Type \"xyz\" and hit Enter: " gets stdin } -result xyz -test console-gets-1.1 {Console file channel: non-blocking gets} {win interactive} { +test console-gets-1.1 {Console file channel: non-blocking gets} -constraints { + win interactive tbd +} -body { set oldmode [fconfigure stdin] - set response [prompt "Type \"abc\" and hit Enter: "] + prompt "Type \"abc\" and hit Enter: " fileevent stdin readable { if {[gets stdin line] >= 0} { set result $line @@ -68,10 +72,55 @@ test console-gets-1.1 {Console file channel: non-blocking gets} {win interactive #cleanup the fileevent fileevent stdin readable {} fconfigure stdin {*}$oldmode - + puts [fconfigure stdin] set result -} abc +} -result abc + +test console-read-1.0 {Console blocking read} -constraints {win interactive} -setup { + set oldmode [fconfigure stdin] + fconfigure stdin -inputmode raw +} -cleanup { + fconfigure stdin {*}$oldmode +} -body { + puts [fconfigure stdin] + prompt "Type the key \"a\". Do NOT hit Enter. You will NOT see characters echoed." + set c [read stdin 1] + puts "" + set c +} -result a + +test console-read-1.1 {Console file channel: non-blocking read} -constraints { + win interactive +} -setup { + set oldmode [fconfigure stdin] +} -cleanup { + fconfigure stdin {*}$oldmode +} -body { + set input "" + fconfigure stdin -blocking 0 -buffering line -inputmode raw + prompt "Type \"abc\". Do NOT hit Enter. You will NOT see characters echoed." + + fileevent stdin readable { + set c [read stdin 1] + if {$c eq ""} { + if {[eof stdin]} { + set result "read eof" + } + } else { + append input $c + if {[string length $input] == 3} { + set result $input + } + } + } + + set result {} + vwait result + fileevent stdin readable {} + puts "" + set result +} -result abc # Output tests @@ -80,16 +129,39 @@ test console-puts-1.0 {Console blocking puts stdout} -constraints {win interacti yesno "Did you see the string \"123\"?" } -result 1 -test console-puts-1.1 {Console blocking puts stderr} -constraints {win interactive} -body { +test console-puts-1.1 {Console non-blocking puts stdout} -constraints { + win interactive +} -setup { + set oldmode [fconfigure stdout] + dict unset oldmode -winsize +} -cleanup { + fconfigure stdout {*}$oldmode +} -body { + fconfigure stdout -blocking 0 -buffering line + set count 0 + fileevent stdout writable { + if {[incr count] < 4} { + puts "$count" + } else { + fileevent stdout writable {} + set done 1 + } + } + vwait done + yesno "Did you see 1, 2, 3 printed on consecutive lines?" +} -result 1 + +test console-puts-2.0 {Console blocking puts stderr} -constraints {win interactive} -body { puts stderr "456" yesno "Did you see the string \"456\"?" } -result 1 -# fconfigure tests -## stdin +# fconfigure get tests + +## fconfigure get stdin -test console-fconfigure-1.0 { +test console-fconfigure-get-1.0 { Console get stdin configuration } -constraints {win interactive} -body { lsort [dict keys [fconfigure stdin]] @@ -104,35 +176,26 @@ foreach {opt result} { -inputmode normal -translation auto } { - test console-fconfigure-1.[incr testnum] "Console get stdin option $opt" \ + test console-fconfigure-get-1.[incr testnum] "Console get stdin option $opt" \ -constraints {win interactive} -body { fconfigure stdin $opt } -result $result } -test console-fconfigure-1.[incr testnum] { +test console-fconfigure-get-1.[incr testnum] { Console get stdin option -eofchar } -constraints {win interactive} -body { fconfigure stdin -eofchar } -result \x1a -test console-fconfigure-1.[incr testnum] { - fconfigure -inputmode password -} -constraints {win interactive} -body { - prompt "Type \"password\" and hit Enter. You should NOT see characters echoed: " - fconfigure stdin -inputmode password - gets stdin password - fconfigure stdin -inputmode normal - set password_echoed [yesno "\nWere the characters echoed?"] - prompt "Type \"normal\" and hit Enter. You should see characters echoed: " - gets stdin normal - set normal_echoed [yesno "Were the characters echoed?"] - list $password_echoed $password $normal_echoed $normal - -} -result [list 0 password 1 normal] +test console-fconfigure-get-1.[incr testnum] { + fconfigure -winsize +} -body { + fconfigure stdin -winsize +} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, -translation, or -inputmode} -returnCodes error -## stdout/stderr +## fconfigure get stdout/stderr foreach chan {stdout stderr} major {2 3} { - test console-fconfigure-$major.0 "Console get $chan configuration" -constraints { + test console-fconfigure-get-$major.0 "Console get $chan configuration" -constraints { win interactive } -body { lsort [dict keys [fconfigure $chan]] @@ -144,23 +207,133 @@ foreach chan {stdout stderr} major {2 3} { -encoding utf-16 -translation crlf } { - test console-fconfigure-$major.[incr testnum] "Console get $chan option $opt" \ + test console-fconfigure-get-$major.[incr testnum] "Console get $chan option $opt" \ -constraints {win interactive} -body { fconfigure $chan $opt } -result $result } - test console-fconfigure-$major.[incr testnum] "Console get $chan option -winsize" -constraints {win interactive} -body { + test console-fconfigure-get-$major.[incr testnum] "Console get $chan option -winsize" -constraints {win interactive} -body { fconfigure $chan -winsize } -result {\d+ \d+} -match regexp - test console-fconfigure-$major.[incr testnum] "Console get $chan option -buffering" -constraints {win interactive} -body { + test console-fconfigure-get-$major.[incr testnum] "Console get $chan option -buffering" -constraints {win interactive} -body { fconfigure $chan -buffering } -result [expr {$chan eq "stdout" ? "line" : "none"}] + + test console-fconfigure-get-$major.[incr testnum] { + fconfigure -inputmode + } -body { + fconfigure $chan -inputmode + } -result {bad option "-inputmode": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, -translation, or -winsize} -returnCodes error + } +## fconfigure set stdin + +test console-fconfigure-set-1.0 { + fconfigure -inputmode password +} -constraints {win interactive} -body { + set result {} + + prompt "Type \"pass\" and hit Enter. You should NOT see characters echoed: " + fconfigure stdin -inputmode password + lappend result [gets stdin] + lappend result [fconfigure stdin -inputmode] + fconfigure stdin -inputmode normal + lappend result [yesno "\nWere the characters echoed?"] + + prompt "Type \"norm\" and hit Enter. You should see characters echoed: " + lappend result [gets stdin] + lappend result [fconfigure stdin -inputmode] + lappend result [yesno "Were the characters echoed?"] + + set result +} -result [list pass password 0 norm normal 1] + +test console-fconfigure-set-1.1 { + fconfigure -inputmode raw +} -constraints {win interactive} -body { + set result {} + + prompt "Type the keys \"a\", Ctrl-H, \"b\". Do NOT hit Enter. You should NOT see characters echoed: " + fconfigure stdin -inputmode raw + lappend result [read stdin 3] + lappend result [fconfigure stdin -inputmode] + fconfigure stdin -inputmode normal + lappend result [yesno "\nWere the characters echoed?"] + + prompt "\nType the keys \"c\", Ctrl-H, \"d\" and hit Enter. You should see characters echoed: " + lappend result [gets stdin] + lappend result [fconfigure stdin -inputmode] + lappend result [yesno "\nWere the characters echoed (c replaced by d)?"] + + set result +} -result [list a\x08b raw 0 d normal 1] + +test console-fconfigure-set-1.2 { + fconfigure -inputmode reset +} -constraints {win interactive} -body { + set result {} + + prompt "Type \"pass\" and hit Enter. You should NOT see characters echoed: " + fconfigure stdin -inputmode password + lappend result [gets stdin] + lappend result [fconfigure stdin -inputmode] + fconfigure stdin -inputmode reset + lappend result [yesno "\nWere the characters echoed?"] + + prompt "Type \"reset\" and hit Enter. You should see characters echoed: " + lappend result [gets stdin] + lappend result [fconfigure stdin -inputmode] + lappend result [yesno "Were the characters echoed?"] + + set result +} -result [list pass password 0 reset normal 1] + +test console-fconfigure-set-1.3 { + fconfigure stdin -winsize +} -body { + fconfigure stdin -winsize {10 30} +} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, -translation, or -inputmode} -returnCodes error + +## fconfigure set stdout,stderr + +test console-fconfigure-set-2.0 { + fconfigure stdout -winsize +} -body { + fconfigure stdout -winsize {10 30} +} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, or -translation} -returnCodes error + +test console-fconfigure-set-3.0 { + fconfigure stderr -winsize +} -body { + fconfigure stderr -winsize {10 30} +} -result {bad option "-winsize": should be one of -blocking, -buffering, -buffersize, -encoding, -eofchar, or -translation} -returnCodes error -#cleanup +# Multiple threads + +test console-thread-input-1.0 {Get input in thread} -constraints { + win interactive haveThread +} -setup { + set tid [thread::create] +} -cleanup { + thread::release $tid +} -body { + prompt "Type \"xyz\" and hit Enter: " + thread::send $tid {gets stdin} +} -result xyz + +test console-thread-output-1.0 {Output from thread} -constraints { + win interactive haveThread +} -setup { + set tid [thread::create] +} -cleanup { + thread::release $tid +} -body { + thread::send $tid {puts [thread::id]} + yesno "Did you see $tid printed?" +} -result 1 ::tcltest::cleanupTests return diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index 4f67b64..2e60c91 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -1014,11 +1014,20 @@ ConsoleCloseProc( */ AcquireSRWLockShared(&handleInfoPtr->lock); - handleInfoPtr->numRefs -= 1; /* Remove reference from this channel */ - handleInfoPtr->console = INVALID_HANDLE_VALUE; + if (closeHandle) { + handleInfoPtr->console = INVALID_HANDLE_VALUE; + } /* Break the thread out of blocking console i/o */ - CancelSynchronousIo(handleInfoPtr->consoleThread); + handleInfoPtr->numRefs -= 1; /* Remove reference from this channel */ + if (handleInfoPtr->numRefs == 1) { + /* + * Abort the i/o if no other threads are listening on it. + * Note without this check, an input line will be skipped on + * the cancel. + */ + CancelSynchronousIo(handleInfoPtr->consoleThread); + } /* * Wake up the console handling thread. Note we do not explicitly @@ -1113,7 +1122,11 @@ ConsoleInputProc( */ if (numRead != 0) { /* If console thread was blocked, awaken it */ - // XXX WakeConditionVariable(&handleInfoPtr->consoleThreadCV); + if (chanInfoPtr->flags & CONSOLE_ASYNC) { + /* Async channels always want read ahead */ + handleInfoPtr->flags |= CONSOLE_DATA_AWAITED; + WakeConditionVariable(&handleInfoPtr->consoleThreadCV); + } break; } /* @@ -1167,6 +1180,11 @@ ConsoleInputProc( } /* Lock is reacquired, loop back to try again */ } + if (chanInfoPtr->flags & CONSOLE_ASYNC) { + /* Async channels always want read ahead */ + handleInfoPtr->flags |= CONSOLE_DATA_AWAITED; + WakeConditionVariable(&handleInfoPtr->consoleThreadCV); + } ReleaseSRWLockExclusive(&handleInfoPtr->lock); return numRead; @@ -2186,12 +2204,13 @@ ConsoleSetOptionProc( return TCL_ERROR; } if (Tcl_UtfNcasecmp(value, "NORMAL", vlen) == 0) { - mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT; + mode |= + ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; } else if (Tcl_UtfNcasecmp(value, "PASSWORD", vlen) == 0) { - mode |= ENABLE_LINE_INPUT; + mode |= ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT; mode &= ~ENABLE_ECHO_INPUT; } else if (Tcl_UtfNcasecmp(value, "RAW", vlen) == 0) { - mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); + mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); } else if (Tcl_UtfNcasecmp(value, "RESET", vlen) == 0) { /* * Reset to the initial mode, whatever that is. |