summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorapnadkarni <apnmbx-wits@yahoo.com>2022-07-05 15:54:00 (GMT)
committerapnadkarni <apnmbx-wits@yahoo.com>2022-07-05 15:54:00 (GMT)
commiteb33dbb186b524be57dd9b4c2ebb9b703cb5080a (patch)
treebecd3f8375a439498e9dfc6f96a91d1c10042b08
parentdda0e7f5f62db678465c31383298f07ab102a2d1 (diff)
downloadtcl-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.test237
-rw-r--r--win/tclWinConsole.c33
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.