summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclClock.c39
-rw-r--r--generic/tclIO.c36
-rw-r--r--generic/tclIORChan.c124
-rw-r--r--generic/tclIntPlatDecls.h8
-rw-r--r--generic/tclStubInit.c18
-rw-r--r--tests/clock.test9
-rw-r--r--tests/ioCmd.test33
-rw-r--r--tests/iogt.test33
-rw-r--r--tests/socket.test35
-rw-r--r--unix/Makefile.in10
-rwxr-xr-xunix/configure3
-rw-r--r--unix/configure.in1
-rw-r--r--unix/tcl.pc.in14
-rw-r--r--win/tclWinFCmd.c5
-rw-r--r--win/tclWinFile.c15
-rw-r--r--win/tclWinInit.c24
-rw-r--r--win/tclWinPort.h9
-rw-r--r--win/tclWinSock.c383
18 files changed, 452 insertions, 347 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index 5b95ae6..3ec94fb 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -548,19 +548,22 @@ ClockGetjuliandayfromerayearmonthdayObjCmd (
}
dict = objv[1];
if (Tcl_DictObjGet(interp, dict, literals[LIT_ERA], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
|| Tcl_GetIndexFromObj(interp, fieldPtr, eras, "era", TCL_EXACT,
&era) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_YEAR],
- &fieldPtr) != TCL_OK
+ || Tcl_DictObjGet(interp, dict, literals[LIT_YEAR], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
|| TclGetIntFromObj(interp, fieldPtr, &(fields.year)) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_MONTH],
- &fieldPtr) != TCL_OK
+ || Tcl_DictObjGet(interp, dict, literals[LIT_MONTH], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
|| TclGetIntFromObj(interp, fieldPtr, &(fields.month)) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_DAYOFMONTH],
- &fieldPtr) != TCL_OK
+ || Tcl_DictObjGet(interp, dict, literals[LIT_DAYOFMONTH], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
|| TclGetIntFromObj(interp, fieldPtr,
&(fields.dayOfMonth)) != TCL_OK
|| TclGetIntFromObj(interp, objv[2], &changeover) != TCL_OK) {
+ if (fieldPtr == NULL)
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("expected key(s) not found in dictionary", -1));
return TCL_ERROR;
}
fields.era = era;
@@ -639,21 +642,21 @@ ClockGetjuliandayfromerayearweekdayObjCmd (
}
dict = objv[1];
if (Tcl_DictObjGet(interp, dict, literals[LIT_ERA], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
|| Tcl_GetIndexFromObj(interp, fieldPtr, eras, "era", TCL_EXACT,
&era) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_ISO8601YEAR],
- &fieldPtr) != TCL_OK
- || TclGetIntFromObj(interp, fieldPtr,
- &(fields.iso8601Year)) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_ISO8601WEEK],
- &fieldPtr) != TCL_OK
- || TclGetIntFromObj(interp, fieldPtr,
- &(fields.iso8601Week)) != TCL_OK
- || Tcl_DictObjGet(interp, dict, literals[LIT_DAYOFWEEK],
- &fieldPtr) != TCL_OK
- || TclGetIntFromObj(interp, fieldPtr,
- &(fields.dayOfWeek)) != TCL_OK
+ || Tcl_DictObjGet(interp, dict, literals[LIT_ISO8601YEAR], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
+ || TclGetIntFromObj(interp, fieldPtr, &(fields.iso8601Year)) != TCL_OK
+ || Tcl_DictObjGet(interp, dict, literals[LIT_ISO8601WEEK], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
+ || TclGetIntFromObj(interp, fieldPtr, &(fields.iso8601Week)) != TCL_OK
+ || Tcl_DictObjGet(interp, dict, literals[LIT_DAYOFWEEK], &fieldPtr) != TCL_OK
+ || fieldPtr == NULL
+ || TclGetIntFromObj(interp, fieldPtr, &(fields.dayOfWeek)) != TCL_OK
|| TclGetIntFromObj(interp, objv[2], &changeover) != TCL_OK) {
+ if (fieldPtr == NULL)
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("expected key(s) not found in dictionary", -1));
return TCL_ERROR;
}
fields.era = era;
diff --git a/generic/tclIO.c b/generic/tclIO.c
index e7653f6..2b0a28b 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -3209,7 +3209,17 @@ Tcl_Close(
Tcl_SetObjResult(interp,
Tcl_NewStringObj(Tcl_PosixError(interp), -1));
}
- flushcode = -1;
+ return TCL_ERROR;
+ }
+ /*
+ * Bug 97069ea11a: set error message if a flush code is set and no error
+ * message set up to now.
+ */
+ if (flushcode != 0 && interp != NULL
+ && 0 == Tcl_GetCharLength(Tcl_GetObjResult(interp)) ) {
+ Tcl_SetErrno(flushcode);
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(Tcl_PosixError(interp), -1));
}
if ((flushcode != 0) || (result != 0)) {
return TCL_ERROR;
@@ -3850,6 +3860,7 @@ Tcl_GetsObj(
*/
chanPtr = statePtr->topChanPtr;
+ Tcl_Preserve(chanPtr);
bufPtr = statePtr->inQueueHead;
encoding = statePtr->encoding;
@@ -4142,6 +4153,7 @@ Tcl_GetsObj(
done:
UpdateInterest(chanPtr);
+ Tcl_Release(chanPtr);
return copiedTotal;
}
@@ -4187,6 +4199,7 @@ TclGetsObjBinary(
*/
chanPtr = statePtr->topChanPtr;
+ Tcl_Preserve(chanPtr);
bufPtr = statePtr->inQueueHead;
@@ -4386,6 +4399,7 @@ TclGetsObjBinary(
done:
UpdateInterest(chanPtr);
+ Tcl_Release(chanPtr);
return copiedTotal;
}
@@ -4857,6 +4871,7 @@ Tcl_ReadRaw(
* requests more bytes.
*/
+ Tcl_Preserve(chanPtr);
for (copied = 0; copied < bytesToRead; copied += copiedNow) {
copiedNow = CopyBuffer(chanPtr, bufPtr + copied,
bytesToRead - copied);
@@ -4907,7 +4922,7 @@ Tcl_ReadRaw(
* over EAGAIN/WOULDBLOCK handling.
*/
- return copied;
+ goto done;
}
SetFlag(statePtr, CHANNEL_BLOCKED);
@@ -4915,14 +4930,17 @@ Tcl_ReadRaw(
}
Tcl_SetErrno(result);
- return -1;
+ copied = -1;
+ goto done;
}
- return copied + nread;
+ copied += nread;
+ goto done;
}
}
done:
+ Tcl_Release(chanPtr);
return copied;
}
@@ -5031,6 +5049,7 @@ DoReadChars(
chanPtr = statePtr->topChanPtr;
encoding = statePtr->encoding;
factor = UTF_EXPANSION_FACTOR;
+ Tcl_Preserve(chanPtr);
binaryMode = (encoding == NULL)
&& (statePtr->inputTranslation == TCL_TRANSLATE_LF)
@@ -5113,6 +5132,7 @@ DoReadChars(
done:
UpdateInterest(chanPtr);
+ Tcl_Release(chanPtr);
return copied;
}
@@ -7587,6 +7607,11 @@ UpdateInterest(
/* State info for channel */
int mask = statePtr->interestMask;
+ if (chanPtr->typePtr == NULL) {
+ /* Do not update interest on a closed channel */
+ return;
+ }
+
/*
* If there are flushed buffers waiting to be written, then we need to
* watch for the channel to become writable.
@@ -8651,6 +8676,7 @@ DoRead(
ChannelState *statePtr = chanPtr->state;
char *p = dst;
+ Tcl_Preserve(chanPtr);
while (bytesToRead) {
/*
* Each pass through the loop is intended to process up to
@@ -8690,6 +8716,7 @@ DoRead(
if (code) {
/* Read error */
UpdateInterest(chanPtr);
+ Tcl_Release(chanPtr);
return -1;
}
@@ -8784,6 +8811,7 @@ DoRead(
}
}
+ Tcl_Release(chanPtr);
return (int)(p - dst);
}
diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c
index ca3ab4b..eaabdfb 100644
--- a/generic/tclIORChan.c
+++ b/generic/tclIORChan.c
@@ -91,25 +91,7 @@ typedef struct {
#ifdef TCL_THREADS
Tcl_ThreadId thread; /* Thread the 'interp' belongs to. */
#endif
-
- /* See [==] as well.
- * Storage for the command prefix and the additional words required for
- * the invocation of methods in the command handler.
- *
- * argv [0] ... [.] | [argc-2] [argc-1] | [argc] [argc+2]
- * cmd ... pfx | method chan | detail1 detail2
- * ~~~~ CT ~~~ ~~ CT ~~
- *
- * CT = Belongs to the 'Command handler Thread'.
- */
-
- int argc; /* Number of preallocated words - 2 */
- Tcl_Obj **argv; /* Preallocated array for calling the handler.
- * args[0] is placeholder for cmd word.
- * Followed by the arguments in the prefix,
- * plus 4 placeholders for method, channel,
- * and at most two varying (method specific)
- * words. */
+ Tcl_Obj *cmd; /* Callback command prefix */
int methods; /* Bitmask of supported methods */
/*
@@ -571,6 +553,7 @@ TclChanCreateObjCmd(
chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr,
mode);
rcPtr->chan = chan;
+ Tcl_Preserve(chan);
chanPtr = (Channel *) chan;
/*
@@ -2021,8 +2004,6 @@ NewReflectedChannel(
Tcl_Obj *handleObj)
{
ReflectedChannel *rcPtr;
- int i, listc;
- Tcl_Obj **listv;
rcPtr = (ReflectedChannel *) ckalloc(sizeof(ReflectedChannel));
@@ -2038,54 +2019,11 @@ NewReflectedChannel(
rcPtr->mode = mode;
rcPtr->interest = 0; /* Initially no interest registered */
- /*
- * Method placeholder.
- */
-
/* ASSERT: cmdpfxObj is a Tcl List */
-
- Tcl_ListObjGetElements(interp, cmdpfxObj, &listc, &listv);
-
- /*
- * See [==] as well.
- * Storage for the command prefix and the additional words required for
- * the invocation of methods in the command handler.
- *
- * listv [0] [listc-1] | [listc] [listc+1] |
- * argv [0] ... [.] | [argc-2] [argc-1] | [argc] [argc+2]
- * cmd ... pfx | method chan | detail1 detail2
- */
-
- rcPtr->argc = listc + 2;
- rcPtr->argv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * (listc+4));
-
- /*
- * Duplicate object references.
- */
-
- for (i=0; i<listc ; i++) {
- Tcl_Obj *word = rcPtr->argv[i] = listv[i];
-
- Tcl_IncrRefCount(word);
- }
-
- i++; /* Skip placeholder for method */
-
- /*
- * [Bug 1667990]: See [x] in FreeReflectedChannel for release
- */
-
- rcPtr->argv[i] = handleObj;
- Tcl_IncrRefCount(handleObj);
-
- /*
- * The next two objects are kept empty, varying arguments.
- */
-
- /*
- * Initialization complete.
- */
-
+ rcPtr->cmd = TclListObjCopy(NULL, cmdpfxObj);
+ Tcl_ListObjAppendElement(NULL, rcPtr->cmd, Tcl_NewObj());
+ Tcl_ListObjAppendElement(NULL, rcPtr->cmd, handleObj);
+ Tcl_IncrRefCount(rcPtr->cmd);
return rcPtr;
}
@@ -2136,7 +2074,6 @@ FreeReflectedChannel(
ReflectedChannel *rcPtr)
{
Channel *chanPtr = (Channel *) rcPtr->chan;
- int i, n;
if (chanPtr->typePtr != &tclRChannelType) {
/*
@@ -2145,19 +2082,8 @@ FreeReflectedChannel(
ckfree((char*) chanPtr->typePtr);
}
-
- n = rcPtr->argc - 2;
- for (i=0; i<n; i++) {
- Tcl_DecrRefCount(rcPtr->argv[i]);
- }
-
- /*
- * [Bug 1667990]: See [x] in NewReflectedChannel for lock. n+1 = argc-1.
- */
-
- Tcl_DecrRefCount(rcPtr->argv[n+1]);
-
- ckfree((char*) rcPtr->argv);
+ Tcl_Release(chanPtr);
+ Tcl_DecrRefCount(rcPtr->cmd);
ckfree((char*) rcPtr);
}
@@ -2193,11 +2119,12 @@ InvokeTclMethod(
Tcl_Obj *argTwoObj, /* NULL'able */
Tcl_Obj **resultObjPtr) /* NULL'able */
{
- int cmdc; /* #words in constructed command */
Tcl_Obj *methObj = NULL; /* Method name in object form */
Tcl_InterpState sr; /* State of handler interp */
int result; /* Result code of method invokation */
Tcl_Obj *resObj = NULL; /* Result of method invokation. */
+ Tcl_Obj *cmd;
+ int len;
if (!rcPtr->interp) {
/*
@@ -2226,26 +2153,24 @@ InvokeTclMethod(
*/
/*
- * Insert method into the pre-allocated area, after the command prefix,
+ * Insert method into the callback command, after the command prefix,
* before the channel id.
*/
methObj = Tcl_NewStringObj(method, -1);
- Tcl_IncrRefCount(methObj);
- rcPtr->argv[rcPtr->argc - 2] = methObj;
+ cmd = TclListObjCopy(NULL, rcPtr->cmd);
+ ListObjLength(cmd, len);
+ Tcl_ListObjReplace(NULL, cmd, len - 2, 1, 1, &methObj);
/*
* Append the additional argument containing method specific details
* behind the channel id. If specified.
*/
- cmdc = rcPtr->argc;
if (argOneObj) {
- rcPtr->argv[cmdc] = argOneObj;
- cmdc++;
+ Tcl_ListObjAppendElement(NULL, cmd, argOneObj);
if (argTwoObj) {
- rcPtr->argv[cmdc] = argTwoObj;
- cmdc++;
+ Tcl_ListObjAppendElement(NULL, cmd, argTwoObj);
}
}
@@ -2254,9 +2179,10 @@ InvokeTclMethod(
* existing state intact.
*/
+ Tcl_IncrRefCount(cmd);
sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */);
Tcl_Preserve(rcPtr->interp);
- result = Tcl_EvalObjv(rcPtr->interp, cmdc, rcPtr->argv, TCL_EVAL_GLOBAL);
+ result = Tcl_GlobalEvalObj(rcPtr->interp, cmd);
/*
* We do not try to extract the result information if the caller has no
@@ -2282,7 +2208,6 @@ InvokeTclMethod(
*/
if (result != TCL_ERROR) {
- Tcl_Obj *cmd = Tcl_NewListObj(cmdc, rcPtr->argv);
int cmdLen;
const char *cmdString = Tcl_GetStringFromObj(cmd, &cmdLen);
@@ -2301,20 +2226,11 @@ InvokeTclMethod(
}
Tcl_IncrRefCount(resObj);
}
+ Tcl_DecrRefCount(cmd);
Tcl_RestoreInterpState(rcPtr->interp, sr);
Tcl_Release(rcPtr->interp);
/*
- * Cleanup of the dynamic parts of the command.
- *
- * The detail objects survived the Tcl_EvalObjv without change because of
- * the contract. Therefore there is no need to decrement the refcounts. Only
- * the internal method object has to be disposed of.
- */
-
- Tcl_DecrRefCount(methObj);
-
- /*
* The resObj has a ref count of 1 at this location. This means that the
* caller of InvokeTclMethod has to dispose of it (but only if it was
* returned to it).
@@ -2837,7 +2753,7 @@ ForwardProc(
}
/*
- * Freeing is done here, in the origin thread, because the argv[]
+ * Freeing is done here, in the origin thread, callback command
* objects belong to this thread. Deallocating them in a different
* thread is not allowed
*
diff --git a/generic/tclIntPlatDecls.h b/generic/tclIntPlatDecls.h
index 80dd2ad..fc20d09 100644
--- a/generic/tclIntPlatDecls.h
+++ b/generic/tclIntPlatDecls.h
@@ -847,9 +847,15 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr;
#undef TclpLocaltime_unix
#undef TclpGmtime_unix
-#if defined(__WIN32__) || defined(__CYGWIN__)
+#if defined(__WIN32__)
# undef TclWinNToHS
+# undef TclWinGetServByName
+# undef TclWinGetSockOpt
+# undef TclWinSetSockOpt
# define TclWinNToHS ntohs
+# define TclWinGetServByName getservbyname
+# define TclWinGetSockOpt getsockopt
+# define TclWinSetSockOpt setsockopt
#else
# undef TclpGetPid
# define TclpGetPid(pid) ((unsigned long) (pid))
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index 99f3e4b..6499bc2 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -33,6 +33,9 @@
#undef Tcl_CreateHashEntry
#undef TclpGetPid
#undef TclSockMinimumBuffers
+#undef TclWinGetServByName
+#undef TclWinGetSockOpt
+#undef TclWinSetSockOpt
#define TclUnusedStubEntry NULL
/*
@@ -104,7 +107,8 @@ TclpIsAtty(int fd)
return isatty(fd);
}
-int
+#define TclWinGetPlatformId winGetPlatformId
+static int
TclWinGetPlatformId()
{
/* Don't bother to determine the real platform on cygwin,
@@ -120,27 +124,31 @@ void *TclWinGetTclInstance()
return hInstance;
}
-int
+#define TclWinSetSockOpt winSetSockOpt
+static int
TclWinSetSockOpt(SOCKET s, int level, int optname,
const char *optval, int optlen)
{
return setsockopt((int) s, level, optname, optval, optlen);
}
-int
+#define TclWinGetSockOpt winGetSockOpt
+static int
TclWinGetSockOpt(SOCKET s, int level, int optname,
char *optval, int *optlen)
{
return getsockopt((int) s, level, optname, optval, optlen);
}
-struct servent *
+#define TclWinGetServByName winGetServByName
+static struct servent *
TclWinGetServByName(const char *name, const char *proto)
{
return getservbyname(name, proto);
}
-char *
+#define TclWinNoBackslash winNoBackslash
+static char *
TclWinNoBackslash(char *path)
{
char *p;
diff --git a/tests/clock.test b/tests/clock.test
index fea1fc9..7d62a60 100644
--- a/tests/clock.test
+++ b/tests/clock.test
@@ -36927,6 +36927,15 @@ test clock-67.1 {clock format, %% with a letter following [Bug 2819334]} {
clock format [clock seconds] -format %%r
} %r
+test clock-67.2 {Bug d19a30db57} -body {
+ # error, not segfault
+ tcl::clock::GetJulianDayFromEraYearMonthDay {} 2361222
+} -returnCodes error -match glob -result *
+test clock-67.3 {Bug d19a30db57} -body {
+ # error, not segfault
+ tcl::clock::GetJulianDayFromEraYearWeekDay {} 2361222
+} -returnCodes error -match glob -result *
+
# cleanup
namespace delete ::testClock
diff --git a/tests/ioCmd.test b/tests/ioCmd.test
index 768a748..f021ade 100644
--- a/tests/ioCmd.test
+++ b/tests/ioCmd.test
@@ -755,6 +755,25 @@ test iocmd-21.19 {chan create, init failure -> no channel, no finalize} -match g
rename foo {}
set res
} -result {{} {initialize rc* {read write}} 1 {*all required methods*} {}}
+test iocmd-21.20 {Bug 88aef05cda} -setup {
+ proc foo {method chan args} {
+ switch -- $method blocking {
+ chan configure $chan -blocking [lindex $args 0]
+ return
+ } initialize {
+ return {initialize finalize watch blocking read write
+ configure cget cgetall}
+ } finalize {
+ return
+ }
+ }
+ set ch [chan create {read write} foo]
+} -body {
+ list [catch {chan configure $ch -blocking 0} m] $m
+} -cleanup {
+ close $ch
+ rename foo {}
+} -match glob -result {1 {*nested eval*}}
# --- --- --- --------- --------- ---------
# Helper commands to record the arguments to handler methods.
@@ -1013,6 +1032,20 @@ test iocmd-23.10 {chan read, EAGAIN means no data, yet no eof either} -match glo
rename foo {}
unset res
} -result {{read rc* 4096} {} 0}
+test iocmd-23.11 {chan read, close pulls the rug out} -match glob -body {
+ set res {}
+ proc foo {args} {
+ oninit; onfinal; track
+ set args [lassign $args sub id]
+ if {$sub ne "read"} {return}
+ close $id
+ return {}
+ }
+ set c [chan create {r} foo]
+ note [read $c]
+ rename foo {}
+ set res
+} -result {{read rc* 4096} {}}
# --- === *** ###########################
# method write
diff --git a/tests/iogt.test b/tests/iogt.test
index 3882ecc..d54ae04 100644
--- a/tests/iogt.test
+++ b/tests/iogt.test
@@ -242,6 +242,26 @@ proc id_fulltrail {var op data} {
return $res
}
+proc id_torture {chan op data} {
+ switch -- $op {
+ create/write -
+ create/read -
+ delete/write -
+ delete/read -
+ clear_read {;#ignore}
+ flush/write -
+ flush/read -
+ write -
+ read {
+ testchannel unstack $chan
+ testchannel transform $chan \
+ -command [namespace code [list id_torture $chan]]
+ return $data
+ }
+ query/maxRead {return -1}
+ }
+}
+
proc counter {var op data} {
variable $var
upvar 0 $var n
@@ -364,6 +384,10 @@ proc audit_flow {var -attach channel} {
testchannel transform $channel -command [namespace code [list id_fulltrail $var]]
}
+proc torture {-attach channel} {
+ testchannel transform $channel -command [namespace code [list id_torture $channel]]
+}
+
proc stopafter {var n -attach channel} {
variable $var
upvar 0 $var vn
@@ -632,6 +656,15 @@ delete/read {} *ignored*
flush/write {} {}
delete/write {} *ignored*}
+test iogt-2.4 {basic I/O, mixed trail} {testchannel} {
+ set fh [open $path(dummy) r]
+ torture -attach $fh
+ chan configure $fh -buffersize 2
+ set x [read $fh]
+ testchannel unstack $fh
+ close $fh
+ set x
+} {}
test iogt-3.0 {Tcl_Channel valid after stack/unstack, fevent handling} \
{testchannel unknownFailure} {
diff --git a/tests/socket.test b/tests/socket.test
index 0ae5abd..1b7c5fa 100644
--- a/tests/socket.test
+++ b/tests/socket.test
@@ -67,6 +67,10 @@ namespace import -force ::tcltest::*
testConstraint testthread [llength [info commands testthread]]
testConstraint exec [llength [info commands exec]]
+# Produce a random port number in the Dynamic/Private range
+# from 49152 through 65535.
+proc randport {} { expr {int(rand()*16383+49152)} }
+
# If remoteServerIP or remoteServerPort are not set, check in the
# environment variables for externally set values.
#
@@ -1683,6 +1687,37 @@ if {[string match sock* $commandSocket] == 1} {
}
catch {close $commandSocket}
catch {close $remoteProcChan}
+test socket-14.13 {testing writable event when quick failure} -constraints {socket win supported_inet} -body {
+ # Test for bug 336441ed59 where a quick background fail was ignored
+
+ # Test only for windows as socket -async 255.255.255.255 fails
+ # directly on unix
+
+ # The following connect should fail very quickly
+ set a1 [after 2000 {set x timeout}]
+ set s [socket -async 255.255.255.255 43434]
+ fileevent $s writable {set x writable}
+ vwait x
+ set x
+} -cleanup {
+ catch {close $s}
+ after cancel $a1
+} -result writable
+
+test socket-14.14 {testing fileevent readable on failed async socket connect} -constraints [list socket] -body {
+ # Test for bug 581937ab1e
+
+ set a1 [after 5000 {set x timeout}]
+ # This connect should fail
+ set s [socket -async localhost [randport]]
+ fileevent $s readable {set x readable}
+ vwait x
+ set x
+} -cleanup {
+ catch {close $s}
+ after cancel $a1
+} -result readable
+
::tcltest::cleanupTests
flush stdout
return
diff --git a/unix/Makefile.in b/unix/Makefile.in
index c7caf5b..746abde 100644
--- a/unix/Makefile.in
+++ b/unix/Makefile.in
@@ -736,6 +736,9 @@ install-binaries: binaries
@INSTALL_STUB_LIB@ ; \
fi
@EXTRA_INSTALL_BINARIES@
+ @echo "Installing pkg-config file to $(LIB_INSTALL_DIR)/pkgconfig/"
+ @mkdir -p $(LIB_INSTALL_DIR)/pkgconfig
+ @$(INSTALL_DATA) tcl.pc $(LIB_INSTALL_DIR)/pkgconfig/tcl.pc
install-libraries: libraries $(INSTALL_TZDATA) install-msgs
@for i in "$(INCLUDE_INSTALL_DIR)" "$(SCRIPT_INSTALL_DIR)"; \
@@ -905,7 +908,8 @@ clean:
distclean: clean
rm -rf Makefile config.status config.cache config.log tclConfig.sh \
- $(PACKAGE).* prototype tclConfig.h *.plist Tcl.framework
+ $(PACKAGE).* prototype tclConfig.h *.plist Tcl.framework \
+ tcl.pc
cd dltest ; $(MAKE) distclean
depend:
@@ -1657,7 +1661,7 @@ $(UNIX_DIR)/tclConfig.h.in: $(MAC_OSX_DIR)/configure
cd $(MAC_OSX_DIR); autoheader; touch $@
EOLFIX=$(NATIVE_TCLSH) $(TOOL_DIR)/eolFix.tcl
-dist: $(UNIX_DIR)/configure $(UNIX_DIR)/tclConfig.h.in $(MAC_OSX_DIR)/configure genstubs
+dist: $(UNIX_DIR)/configure $(UNIX_DIR)/tclConfig.h.in $(UNIX_DIR)/tcl.pc.in $(MAC_OSX_DIR)/configure genstubs
rm -rf $(DISTDIR)
mkdir -p $(DISTDIR)/unix
cp -p $(UNIX_DIR)/*.[ch] $(DISTDIR)/unix
@@ -1668,7 +1672,7 @@ dist: $(UNIX_DIR)/configure $(UNIX_DIR)/tclConfig.h.in $(MAC_OSX_DIR)/configure
$(UNIX_DIR)/tclConfig.sh.in $(UNIX_DIR)/install-sh \
$(UNIX_DIR)/README $(UNIX_DIR)/ldAix $(UNIX_DIR)/tcl.spec \
$(UNIX_DIR)/installManPage $(UNIX_DIR)/tclConfig.h.in \
- $(DISTDIR)/unix
+ $(UNIX_DIR)/tcl.pc.in $(DISTDIR)/unix
chmod 775 $(DISTDIR)/unix/configure $(DISTDIR)/unix/configure.in
chmod 775 $(DISTDIR)/unix/ldAix
@mkdir $(DISTDIR)/generic
diff --git a/unix/configure b/unix/configure
index 1b2ea41..02a3725 100755
--- a/unix/configure
+++ b/unix/configure
@@ -18915,7 +18915,7 @@ TCL_SHARED_BUILD=${SHARED_BUILD}
- ac_config_files="$ac_config_files Makefile:../unix/Makefile.in dltest/Makefile:../unix/dltest/Makefile.in tclConfig.sh:../unix/tclConfig.sh.in"
+ ac_config_files="$ac_config_files Makefile:../unix/Makefile.in dltest/Makefile:../unix/dltest/Makefile.in tclConfig.sh:../unix/tclConfig.sh.in tcl.pc:../unix/tcl.pc.in"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -19469,6 +19469,7 @@ do
"Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile:../unix/Makefile.in" ;;
"dltest/Makefile" ) CONFIG_FILES="$CONFIG_FILES dltest/Makefile:../unix/dltest/Makefile.in" ;;
"tclConfig.sh" ) CONFIG_FILES="$CONFIG_FILES tclConfig.sh:../unix/tclConfig.sh.in" ;;
+ "tcl.pc" ) CONFIG_FILES="$CONFIG_FILES tcl.pc:../unix/tcl.pc.in" ;;
"Tcl.framework" ) CONFIG_COMMANDS="$CONFIG_COMMANDS Tcl.framework" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
diff --git a/unix/configure.in b/unix/configure.in
index b5a09dd..318bcf8 100644
--- a/unix/configure.in
+++ b/unix/configure.in
@@ -940,5 +940,6 @@ AC_CONFIG_FILES([
Makefile:../unix/Makefile.in
dltest/Makefile:../unix/dltest/Makefile.in
tclConfig.sh:../unix/tclConfig.sh.in
+ tcl.pc:../unix/tcl.pc.in
])
AC_OUTPUT
diff --git a/unix/tcl.pc.in b/unix/tcl.pc.in
new file mode 100644
index 0000000..b750300
--- /dev/null
+++ b/unix/tcl.pc.in
@@ -0,0 +1,14 @@
+# tcl pkg-config source file
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Tool Command Language
+Description: Tcl is a powerful, easy-to-learn dynamic programming language, suitable for a wide range of uses.
+URL: http://www.tcl.tk/
+Version: @TCL_VERSION@@TCL_PATCH_LEVEL@
+Libs: -L${libdir} @TCL_LIB_FLAG@
+Libs.private: @TCL_LIBS@
+Cflags: -I${includedir}
diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c
index 8999831..441337e 100644
--- a/win/tclWinFCmd.c
+++ b/win/tclWinFCmd.c
@@ -1156,7 +1156,12 @@ DoRemoveJustDirectory(
end:
if (errorPtr != NULL) {
+ char *p;
Tcl_WinTCharToUtf(nativePath, -1, errorPtr);
+ p = Tcl_DStringValue(errorPtr);
+ for (; *p; ++p) {
+ if (*p == '\\') *p = '/';
+ }
}
return TCL_ERROR;
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index 676c443..ed0c40f 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -3197,17 +3197,14 @@ TclNativeCreateNativeRep(
}
str = Tcl_GetStringFromObj(validPathPtr, &len);
- if (str[0] == '/' && str[1] == '/' && str[2] == '?' && str[3] == '/') {
- char *p;
-
- for (p = str; p && *p; ++p) {
- if (*p == '/') {
- *p = '\\';
- }
- }
- }
Tcl_WinUtfToTChar(str, len, &ds);
if (tclWinProcs->useWide) {
+ WCHAR *wp = (WCHAR *) Tcl_DStringValue(&ds);
+ for (; *wp; ++wp) {
+ if (*wp=='/') {
+ *wp = '\\';
+ }
+ }
len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
} else {
len = Tcl_DStringLength(&ds) + sizeof(char);
diff --git a/win/tclWinInit.c b/win/tclWinInit.c
index 2f3c7e8..4e860b2 100644
--- a/win/tclWinInit.c
+++ b/win/tclWinInit.c
@@ -113,8 +113,8 @@ static int ToUtf(CONST WCHAR *wSrc, char *dst);
*
* TclpInitPlatform --
*
- * Initialize all the platform-dependant things like signals and
- * floating-point error handling.
+ * Initialize all the platform-dependant things like signals,
+ * floating-point error handling and sockets.
*
* Called at process initialization time.
*
@@ -130,20 +130,16 @@ static int ToUtf(CONST WCHAR *wSrc, char *dst);
void
TclpInitPlatform(void)
{
- tclPlatform = TCL_PLATFORM_WINDOWS;
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
- /*
- * The following code stops Windows 3.X and Windows NT 3.51 from
- * automatically putting up Sharing Violation dialogs, e.g, when someone
- * tries to access a file that is locked or a drive with no disk in it.
- * Tcl already returns the appropriate error to the caller, and they can
- * decide to put up their own dialog in response to that failure.
- *
- * Under 95 and NT 4.0, this is a NOOP because the system doesn't
- * automatically put up dialogs when the above operations fail.
- */
+ tclPlatform = TCL_PLATFORM_WINDOWS;
- SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS);
+ /*
+ * Initialize the winsock library. On Windows XP and higher this
+ * can never fail.
+ */
+ WSAStartup(wVersionRequested, &wsaData);
#ifdef STATIC_BUILD
/*
diff --git a/win/tclWinPort.h b/win/tclWinPort.h
index ec9e867..ea6d8f8 100644
--- a/win/tclWinPort.h
+++ b/win/tclWinPort.h
@@ -448,15 +448,6 @@ typedef DWORD_PTR * PDWORD_PTR;
#define TclpSysRealloc(ptr, size) ((void*)HeapReAlloc(GetProcessHeap(), \
(DWORD)0, (LPVOID)ptr, (DWORD)size))
-/*
- * The following defines map from standard socket names to our internal
- * wrappers that redirect through the winSock function table (see the
- * file tclWinSock.c).
- */
-
-#define getservbyname TclWinGetServByName
-#define getsockopt TclWinGetSockOpt
-#define setsockopt TclWinSetSockOpt
/* This type is not defined in the Windows headers */
#define socklen_t int
diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index 9fa01c9..e18a3dd 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -98,29 +98,31 @@ static ProcessGlobalValue hostName = {
/*
* The following structure is used to store the data associated with each
* socket.
+ * All members modified by the notifier thread are defined as volatile.
*/
typedef struct SocketInfo {
Tcl_Channel channel; /* Channel associated with this socket. */
SOCKET socket; /* Windows SOCKET handle. */
- int flags; /* Bit field comprised of the flags described
+ volatile int flags; /* Bit field comprised of the flags described
* below. */
int watchEvents; /* OR'ed combination of FD_READ, FD_WRITE,
* FD_CLOSE, FD_ACCEPT and FD_CONNECT that
* indicate which events are interesting. */
- int readyEvents; /* OR'ed combination of FD_READ, FD_WRITE,
+ volatile int readyEvents; /* OR'ed combination of FD_READ, FD_WRITE,
* FD_CLOSE, FD_ACCEPT and FD_CONNECT that
* indicate which events have occurred. */
int selectEvents; /* OR'ed combination of FD_READ, FD_WRITE,
* FD_CLOSE, FD_ACCEPT and FD_CONNECT that
* indicate which events are currently being
* selected. */
- int acceptEventCount; /* Count of the current number of FD_ACCEPTs
+ volatile int acceptEventCount;
+ /* Count of the current number of FD_ACCEPTs
* that have arrived and not yet processed. */
Tcl_TcpAcceptProc *acceptProc;
/* Proc to call on accept. */
ClientData acceptProcData; /* The data for the accept proc. */
- int lastError; /* Error code from last message. */
+ volatile int lastError; /* Error code from last message. */
struct SocketInfo *nextPtr; /* The next socket on the per-thread socket
* list. */
} SocketInfo;
@@ -167,6 +169,10 @@ typedef struct {
* socketThread has been initialized and has
* started. */
HANDLE socketListLock; /* Win32 Event to lock the socketList */
+ SocketInfo *pendingSocketInfo;
+ /* This socket is opened but not jet in the
+ * list. This value is also checked by
+ * the event structure. */
SocketInfo *socketList; /* Every open socket in this thread has an
* entry on this list. */
} ThreadSpecificData;
@@ -257,8 +263,6 @@ static void
InitSockets(void)
{
DWORD id;
- WSADATA wsaData;
- DWORD err;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
TclThreadDataKeyGet(&dataKey);
@@ -289,38 +293,6 @@ InitSockets(void)
goto initFailure;
}
- /*
- * Initialize the winsock library and check the interface version
- * actually loaded. We only ask for the 1.1 interface and do require
- * that it not be less than 1.1.
- */
-
-#define WSA_VERSION_MAJOR 1
-#define WSA_VERSION_MINOR 1
-#define WSA_VERSION_REQD MAKEWORD(WSA_VERSION_MAJOR, WSA_VERSION_MINOR)
-
- err = WSAStartup((WORD)WSA_VERSION_REQD, &wsaData);
- if (err != 0) {
- TclWinConvertWSAError(err);
- goto initFailure;
- }
-
- /*
- * Note the byte positions are swapped for the comparison, so that
- * 0x0002 (2.0, MAKEWORD(2,0)) doesn't look less than 0x0101 (1.1).
- * We want the comparison to be 0x0200 < 0x0101.
- */
-
- if (MAKEWORD(HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion))
- < MAKEWORD(WSA_VERSION_MINOR, WSA_VERSION_MAJOR)) {
- TclWinConvertWSAError(WSAVERNOTSUPPORTED);
- WSACleanup();
- goto initFailure;
- }
-
-#undef WSA_VERSION_REQD
-#undef WSA_VERSION_MAJOR
-#undef WSA_VERSION_MINOR
}
/*
@@ -329,6 +301,7 @@ InitSockets(void)
if (tsdPtr == NULL) {
tsdPtr = TCL_TSD_INIT(&dataKey);
+ tsdPtr->pendingSocketInfo = NULL;
tsdPtr->socketList = NULL;
tsdPtr->hwnd = NULL;
tsdPtr->threadId = Tcl_GetCurrentThread();
@@ -427,7 +400,6 @@ SocketExitHandler(
TclpFinalizeSockets();
UnregisterClass("TclSocket", TclWinGetTclInstance());
- WSACleanup();
initialized = 0;
Tcl_MutexUnlock(&socketMutex);
}
@@ -712,44 +684,52 @@ SocketEventProc(
Tcl_SetMaxBlockTime(&blockTime);
mask |= TCL_READABLE|TCL_WRITABLE;
} else if (events & FD_READ) {
- fd_set readFds;
- struct timeval timeout;
-
/*
- * We must check to see if data is really available, since someone
- * could have consumed the data in the meantime. Turn off async
- * notification so select will work correctly. If the socket is still
- * readable, notify the channel driver, otherwise reset the async
- * select handler and keep waiting.
+ * Throw the readable event if an async connect failed.
*/
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) UNSELECT, (LPARAM) infoPtr);
-
- FD_ZERO(&readFds);
- FD_SET(infoPtr->socket, &readFds);
- timeout.tv_usec = 0;
- timeout.tv_sec = 0;
+ if (infoPtr->lastError) {
- if (select(0, &readFds, NULL, NULL, &timeout) != 0) {
mask |= TCL_READABLE;
+
} else {
- infoPtr->readyEvents &= ~(FD_READ);
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
- (WPARAM) SELECT, (LPARAM) infoPtr);
- }
- }
- if (events & (FD_WRITE | FD_CONNECT)) {
- mask |= TCL_WRITABLE;
- if (events & FD_CONNECT && infoPtr->lastError != NO_ERROR) {
+ fd_set readFds;
+ struct timeval timeout;
+
/*
- * Connect errors should also fire the readable handler.
+ * We must check to see if data is really available, since someone
+ * could have consumed the data in the meantime. Turn off async
+ * notification so select will work correctly. If the socket is still
+ * readable, notify the channel driver, otherwise reset the async
+ * select handler and keep waiting.
*/
- mask |= TCL_READABLE;
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+ (WPARAM) UNSELECT, (LPARAM) infoPtr);
+
+ FD_ZERO(&readFds);
+ FD_SET(infoPtr->socket, &readFds);
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 0;
+
+ if (select(0, &readFds, NULL, NULL, &timeout) != 0) {
+ mask |= TCL_READABLE;
+ } else {
+ infoPtr->readyEvents &= ~(FD_READ);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
+ (WPARAM) SELECT, (LPARAM) infoPtr);
+ }
}
}
+ /*
+ * writable event
+ */
+
+ if (events & FD_WRITE) {
+ mask |= TCL_WRITABLE;
+ }
+
if (mask) {
Tcl_NotifyChannel(infoPtr->channel, mask);
}
@@ -815,7 +795,7 @@ TcpCloseProc(
SocketInfo *infoPtr = (SocketInfo *) instanceData;
/* TIP #218 */
int errorCode = 0;
- /* ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); */
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
* Check that WinSock is initialized; do not call it if not, to prevent
@@ -837,6 +817,23 @@ TcpCloseProc(
}
/*
+ * Clear an eventual tsd info list pointer.
+ * This may be called, if an async socket connect fails or is closed
+ * between connect and thread action callback.
+ */
+ if (tsdPtr->pendingSocketInfo != NULL
+ && tsdPtr->pendingSocketInfo == infoPtr) {
+
+ /* get infoPtr lock, because this concerns the notifier thread */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ tsdPtr->pendingSocketInfo = NULL;
+
+ /* Free list lock */
+ SetEvent(tsdPtr->socketListLock);
+ }
+
+ /*
* TIP #218. Removed the code removing the structure from the global
* socket list. This is now done by the thread action callbacks, and only
* there. This happens before this code is called. We can free without
@@ -923,12 +920,10 @@ CreateSocket(
* asynchronously. */
{
u_long flag = 1; /* Indicates nonblocking mode. */
- int asyncConnect = 0; /* Will be 1 if async connect is in
- * progress. */
SOCKADDR_IN sockaddr; /* Socket address */
SOCKADDR_IN mysockaddr; /* Socket address for client */
SOCKET sock = INVALID_SOCKET;
- SocketInfo *infoPtr; /* The returned value. */
+ SocketInfo *infoPtr=NULL; /* The returned value. */
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
TclThreadDataKeyGet(&dataKey);
@@ -1007,6 +1002,15 @@ CreateSocket(
infoPtr->selectEvents = FD_ACCEPT;
infoPtr->watchEvents |= FD_ACCEPT;
+ /*
+ * Register for interest in events in the select mask. Note that this
+ * automatically places the socket into non-blocking mode.
+ */
+
+ ioctlsocket(sock, (long) FIONBIO, &flag);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) infoPtr);
+
} else {
/*
* Try to bind to a local port, if specified.
@@ -1020,14 +1024,54 @@ CreateSocket(
}
/*
+ * Allocate socket info structure
+ */
+
+ infoPtr = NewSocketInfo(sock);
+
+ /*
* Set the socket into nonblocking mode if the connect should be done
- * in the background.
+ * in the background. Activate connect notification.
*/
if (async) {
- if (ioctlsocket(sock, (long) FIONBIO, &flag) == SOCKET_ERROR) {
- goto error;
- }
+
+ /* get infoPtr lock */
+ WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+ /*
+ * Buffer new infoPtr in the tsd memory as long as it is not in
+ * the info list. This allows the event procedure to process the
+ * event.
+ * Bugfig for 336441ed59 to not ignore notifications until the
+ * infoPtr is in the list..
+ */
+
+ tsdPtr->pendingSocketInfo = infoPtr;
+
+ /*
+ * Set connect mask to connect events
+ * This is activated by a SOCKET_SELECT message to the notifier
+ * thread.
+ */
+
+ infoPtr->selectEvents |= FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE;
+ infoPtr->flags |= SOCKET_ASYNC_CONNECT;
+
+ /*
+ * Free list lock
+ */
+ SetEvent(tsdPtr->socketListLock);
+
+ /*
+ * Activate accept notification and put in async mode
+ * Bug 336441ed59: activate notification before connect
+ * so we do not miss a notification of a fialed connect.
+ */
+ ioctlsocket(sock, (long) FIONBIO, &flag);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) infoPtr);
+
}
/*
@@ -1045,35 +1089,26 @@ CreateSocket(
* The connection is progressing in the background.
*/
- asyncConnect = 1;
- }
+ } else {
- /*
- * Add this socket to the global list of sockets.
- */
+ /*
+ * Set up the select mask for read/write events. If the connect
+ * attempt has not completed, include connect events.
+ */
- infoPtr = NewSocketInfo(sock);
+ infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
- /*
- * Set up the select mask for read/write events. If the connect
- * attempt has not completed, include connect events.
- */
+ /*
+ * Register for interest in events in the select mask. Note that this
+ * automatically places the socket into non-blocking mode.
+ */
- infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
- if (asyncConnect) {
- infoPtr->flags |= SOCKET_ASYNC_CONNECT;
- infoPtr->selectEvents |= FD_CONNECT;
+ ioctlsocket(sock, (long) FIONBIO, &flag);
+ SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+ (LPARAM) infoPtr);
}
}
- /*
- * Register for interest in events in the select mask. Note that this
- * automatically places the socket into non-blocking mode.
- */
-
- ioctlsocket(sock, (long) FIONBIO, &flag);
- SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, (LPARAM) infoPtr);
-
return infoPtr;
error:
@@ -1082,7 +1117,15 @@ CreateSocket(
Tcl_AppendResult(interp, "couldn't open socket: ",
Tcl_PosixError(interp), NULL);
}
- if (sock != INVALID_SOCKET) {
+ if (infoPtr != NULL) {
+ /*
+ * Free the allocated socket info structure and close the socket
+ */
+ TcpCloseProc(infoPtr, interp);
+ } else if (sock != INVALID_SOCKET) {
+ /*
+ * No socket structure jet - just close
+ */
closesocket(sock);
}
return NULL;
@@ -1482,7 +1525,7 @@ TcpAccept(
SetHandleInformation((HANDLE) newSocket, HANDLE_FLAG_INHERIT, 0);
/*
- * Add this socket to the global list of sockets.
+ * Allocate socket info structure
*/
newInfoPtr = NewSocketInfo(newSocket);
@@ -2248,6 +2291,7 @@ SocketProc(
int event, error;
SOCKET socket;
SocketInfo *infoPtr;
+ int info_found = 0;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
#ifdef _WIN64
GetWindowLongPtr(hwnd, GWLP_USERDATA);
@@ -2293,58 +2337,71 @@ SocketProc(
for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
infoPtr = infoPtr->nextPtr) {
if (infoPtr->socket == socket) {
- /*
- * Update the socket state.
- *
- * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event
- * happens, then clear the FD_ACCEPT count. Otherwise,
- * increment the count if the current event is an FD_ACCEPT.
- */
+ info_found = 1;
+ break;
+ }
+ }
+ /*
+ * Check if there is a pending info structure not jet in the
+ * list
+ */
+ if ( !info_found
+ && tsdPtr->pendingSocketInfo != NULL
+ && tsdPtr->pendingSocketInfo->socket ==socket ) {
+ infoPtr = tsdPtr->pendingSocketInfo;
+ info_found = 1;
+ }
+ if (info_found) {
- if (event & FD_CLOSE) {
- infoPtr->acceptEventCount = 0;
- infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
- } else if (event & FD_ACCEPT) {
- infoPtr->acceptEventCount++;
- }
+ /*
+ * Update the socket state.
+ *
+ * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event
+ * happens, then clear the FD_ACCEPT count. Otherwise,
+ * increment the count if the current event is an FD_ACCEPT.
+ */
- if (event & FD_CONNECT) {
- /*
- * The socket is now connected, clear the async connect
- * flag.
- */
+ if (event & FD_CLOSE) {
+ infoPtr->acceptEventCount = 0;
+ infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
+ } else if (event & FD_ACCEPT) {
+ infoPtr->acceptEventCount++;
+ }
- infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
+ if (event & FD_CONNECT) {
+ /*
+ * The socket is now connected, clear the async connect
+ * flag.
+ */
- /*
- * Remember any error that occurred so we can report
- * connection failures.
- */
+ infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
- if (error != ERROR_SUCCESS) {
- TclWinConvertWSAError((DWORD) error);
- infoPtr->lastError = Tcl_GetErrno();
- }
- }
+ /*
+ * Remember any error that occurred so we can report
+ * connection failures.
+ */
- if (infoPtr->flags & SOCKET_ASYNC_CONNECT) {
- infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
- if (error != ERROR_SUCCESS) {
- TclWinConvertWSAError((DWORD) error);
- infoPtr->lastError = Tcl_GetErrno();
- }
- infoPtr->readyEvents |= FD_WRITE;
+ if (error != ERROR_SUCCESS) {
+ /* Async Connect error */
+ TclWinConvertWSAError((DWORD) error);
+ infoPtr->lastError = Tcl_GetErrno();
+ /* Fire also readable event on connect failure */
+ infoPtr->readyEvents |= FD_READ;
}
- infoPtr->readyEvents |= event;
- /*
- * Wake up the Main Thread.
- */
+ /* fire writable event on connect */
+ infoPtr->readyEvents |= FD_WRITE;
- SetEvent(tsdPtr->readyEvent);
- Tcl_ThreadAlert(tsdPtr->threadId);
- break;
}
+
+ infoPtr->readyEvents |= event;
+
+ /*
+ * Wake up the Main Thread.
+ */
+
+ SetEvent(tsdPtr->readyEvent);
+ Tcl_ThreadAlert(tsdPtr->threadId);
}
SetEvent(tsdPtr->socketListLock);
break;
@@ -2472,71 +2529,34 @@ InitializeHostName(
*----------------------------------------------------------------------
*/
+#undef TclWinGetSockOpt
int
TclWinGetSockOpt(SOCKET s, int level, int optname, char *optval,
int *optlen)
{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return SOCKET_ERROR;
- }
-
return getsockopt(s, level, optname, optval, optlen);
}
+#undef TclWinSetSockOpt
int
TclWinSetSockOpt(SOCKET s, int level, int optname, const char *optval,
int optlen)
{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return SOCKET_ERROR;
- }
-
return setsockopt(s, level, optname, optval, optlen);
}
char *
TclpInetNtoa(struct in_addr addr)
{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return NULL;
- }
-
return inet_ntoa(addr);
}
+#undef TclWinGetServByName
struct servent *
TclWinGetServByName(
const char *name,
const char *proto)
{
- /*
- * Check that WinSock is initialized; do not call it if not, to prevent
- * system crashes. This can happen at exit time if the exit handler for
- * WinSock ran before other exit handlers that want to use sockets.
- */
-
- if (!SocketsEnabled()) {
- return NULL;
- }
-
return getservbyname(name, proto);
}
@@ -2580,6 +2600,11 @@ TcpThreadActionProc(
WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
infoPtr->nextPtr = tsdPtr->socketList;
tsdPtr->socketList = infoPtr;
+
+ if (infoPtr == tsdPtr->pendingSocketInfo) {
+ tsdPtr->pendingSocketInfo = NULL;
+ }
+
SetEvent(tsdPtr->socketListLock);
notifyCmd = SELECT;