Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch fix-1997007 Through [b1d72a2efd] Excluding Merge-Ins
This is equivalent to a diff from 80252e0aed to b1d72a2efd
|
2017-04-12
| ||
| 09:23 | Fix sporadically errors in zlib-8.x and socket tests, cherry-picked from "fix-1997007" branch. Credi... check-in: afd01aab8f user: jan.nijtmans tags: core-8-6-branch | |
|
2017-04-11
| ||
| 18:09 | prepared to use pipe-helpers (TI-structure handling) for all pipe-workers (tclWinConsole, tclWinSeri... check-in: 0ca2abbbb8 user: sebres tags: fix-1997007 | |
| 18:08 | code review and fix small memory leak using ckalloc, without finalization of tcl subsystem in the wo... check-in: b1d72a2efd user: sebres tags: fix-1997007 | |
| 18:08 | shared structures of pipe-workers rewritten using atomic state of the thread; asynchronous start/st... check-in: 1a93b847db user: sebres tags: fix-1997007 | |
|
2017-04-05
| ||
| 10:40 | Contributed by "stanko" as patch within 8bd13f07bde6fb0631f27927e36461fdefe8ca95 Resolves blocking ... check-in: 4a59e2e2ff user: sebres tags: fix-1997007 | |
| 09:32 | Patch for [8bd13f07bde6fb0631f27927e36461fdefe8ca95|8bd13f07bd]: Closing tcl pipes prevents windows ... Closed-Leaf check-in: 2ae28488d5 user: jan.nijtmans tags: bug-8bd13f07bd | |
| 09:09 | Fix [4b12ccb3363e81b132e8dbe12aeec596102be1a8|4b12ccb336]: format/scan %llu doesn't work. Also added... check-in: be99031456 user: jan.nijtmans tags: trunk | |
| 08:41 | Fix [4b12ccb3363e81b132e8dbe12aeec596102be1a8|4b12ccb336]: format/scan %llu doesn't work. Also added... check-in: 80252e0aed user: jan.nijtmans tags: core-8-6-branch | |
|
2017-04-03
| ||
| 11:53 | Code optimization/reduction: If TCL_WIDE_INT_IS_LONG is defined, the variable useWide is always 0, s... check-in: 231e5d408e user: jan.nijtmans tags: core-8-6-branch | |
Changes to win/tclWinConsole.c.
| ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | + - + + |
/*
* Structure containing handles associated with one of the special console
* threads.
*/
typedef struct ConsoleThreadInfo {
HANDLE thread; /* Handle to reader or writer thread. */
int threadExiting; /* Boolean indicating that thread is exiting. */
HANDLE readyEvent; /* Manual-reset event to signal _to_ the main
* thread when the worker thread has finished
* waiting for its normal work to happen. */
HANDLE startEvent; /* Auto-reset event used by the main thread to
* signal when the thread should attempt to do
|
| ︙ | |||
532 533 534 535 536 537 538 539 540 541 542 543 544 545 | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | + |
ConsoleThreadInfo *threadInfoPtr,
LPTHREAD_START_ROUTINE threadProc)
{
DWORD id;
threadInfoPtr->readyEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
threadInfoPtr->startEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
threadInfoPtr->threadExiting = FALSE;
threadInfoPtr->stopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
threadInfoPtr->thread = CreateThread(NULL, 256, threadProc, infoPtr, 0,
&id);
SetThreadPriority(threadInfoPtr->thread, THREAD_PRIORITY_HIGHEST);
}
static void
|
| ︙ | |||
568 569 570 571 572 573 574 575 576 | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | + + + + + + + + + + + + + + + + - - - - + + + + + |
if (WaitForSingleObject(threadInfoPtr->thread, 20) == WAIT_TIMEOUT) {
/*
* Forcibly terminate the background thread as a last resort.
* Note that we need to guard against terminating the thread while
* it is in the middle of Tcl_ThreadAlert because it won't be able
* to release the notifier lock.
*
* Also note that terminating threads during their initialization or teardown phase
* may result in ntdll.dll's LoaderLock to remain locked indefinitely.
* This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock.
* LdrpInitializeThread() is executed within new threads to perform
* initialization and to execute DllMain() of all loaded dlls.
* As a result, all new threads are deadlocked in their initialization phase and never execute,
* even though CreateThread() reports successful thread creation.
* This results in a very weird process-wide behavior, which is extremely hard to debug.
*
* THREADS SHOULD NEVER BE TERMINATED. Period.
*
* But for now, check if thread is exiting, and if so, let it die peacefully.
*/
if ( !threadInfoPtr->threadExiting
|| WaitForSingleObject(threadInfoPtr->thread, 5000) != WAIT_OBJECT_0
) {
|
| ︙ | |||
804 805 806 807 808 809 810 | 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 | - + |
ConsoleOutputProc(
ClientData instanceData, /* Console state. */
const char *buf, /* The data buffer. */
int toWrite, /* How many bytes to write? */
int *errorCode) /* Where to store error code. */
{
ConsoleInfo *infoPtr = instanceData;
|
| ︙ | |||
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 | + + + + + |
{
ConsoleInfo *infoPtr = arg;
HANDLE *handle = infoPtr->handle;
ConsoleThreadInfo *threadInfo = &infoPtr->reader;
DWORD waitResult;
HANDLE wEvents[2];
/*
* Notify caller (using startEvent) that this thread is initialized
*/
SignalObjectAndWait(threadInfo->startEvent, threadInfo->stopEvent, INFINITE, FALSE);
/*
* The first event takes precedence.
*/
wEvents[0] = threadInfo->stopEvent;
wEvents[1] = threadInfo->startEvent;
|
| ︙ | |||
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 | 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 | + + + + + + |
*/
Tcl_ThreadAlert(infoPtr->threadId);
}
Tcl_MutexUnlock(&consoleMutex);
}
/*
* Inform caller that this thread should not be terminated, since it is about to exit.
* See comment in StopChannelThread() for reasons.
*/
threadInfo->threadExiting = TRUE;
return 0;
}
/*
*----------------------------------------------------------------------
*
* ConsoleWriterThread --
|
| ︙ | |||
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 | 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 | + + + + + |
ConsoleInfo *infoPtr = arg;
HANDLE *handle = infoPtr->handle;
ConsoleThreadInfo *threadInfo = &infoPtr->writer;
DWORD count, toWrite, waitResult;
char *buf;
HANDLE wEvents[2];
/*
* Notify caller (using startEvent) that this thread is initialized
*/
SignalObjectAndWait(threadInfo->startEvent, threadInfo->stopEvent, INFINITE, FALSE);
/*
* The first event takes precedence.
*/
wEvents[0] = threadInfo->stopEvent;
wEvents[1] = threadInfo->startEvent;
|
| ︙ | |||
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 | 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 | + + + + + + |
*/
Tcl_ThreadAlert(infoPtr->threadId);
}
Tcl_MutexUnlock(&consoleMutex);
}
/*
* Inform caller that this thread should not be terminated, since it is about to exit.
* See comment in StopChannelThread() for reasons.
*/
threadInfo->threadExiting = TRUE;
return 0;
}
/*
*----------------------------------------------------------------------
*
* TclWinOpenConsoleChannel --
|
| ︙ | |||
1376 1377 1378 1379 1380 1381 1382 | 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 | - + + |
TclWinOpenConsoleChannel(
HANDLE handle,
char *channelName,
int permissions)
{
char encoding[4 + TCL_INTEGER_SPACE];
ConsoleInfo *infoPtr;
|
| ︙ | |||
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 | 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 | + + + + + + + + + + + + + |
*/
GetConsoleMode(infoPtr->handle, &modes);
modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
modes |= ENABLE_LINE_INPUT;
SetConsoleMode(infoPtr->handle, modes);
StartChannelThread(infoPtr, &infoPtr->reader, ConsoleReaderThread);
wEvents[wEventsCnt++] = infoPtr->reader.startEvent;
}
if (permissions & TCL_WRITABLE) {
StartChannelThread(infoPtr, &infoPtr->writer, ConsoleWriterThread);
wEvents[wEventsCnt++] = infoPtr->writer.startEvent;
}
/*
* Wait for both threads to initialize (using theirs startEvent)
*/
if (wEventsCnt) {
WaitForMultipleObjects(wEventsCnt, wEvents, TRUE, 5000);
/* Resume both waiting threads, we've get the events */
if (infoPtr->reader.thread)
SetEvent(infoPtr->reader.stopEvent);
if (infoPtr->writer.thread)
SetEvent(infoPtr->writer.stopEvent);
}
/*
* Files have default translation of AUTO and ^Z eof char, which means
* that a ^Z will be accepted as EOF when reading.
*/
Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
|
| ︙ |
Changes to win/tclWinInit.c.
| ︙ | |||
72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | + + + + + + + + + |
#ifndef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10
#endif
#ifndef PROCESSOR_ARCHITECTURE_UNKNOWN
#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xFFFF
#endif
/*
* Windows version dependend functions
*/
static TclWinProcs _tclWinProcs = {
NULL
};
TclWinProcs *tclWinProcs = &_tclWinProcs;
/*
* The following arrays contain the human readable strings for the Windows
* platform and processor values.
*/
#define NUMPLATFORMS 4
|
| ︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | + + + + + + + + + + + |
*/
void
TclpInitPlatform(void)
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
HINSTANCE hInstance;
tclPlatform = TCL_PLATFORM_WINDOWS;
/*
* Initialize the winsock library. On Windows XP and higher this
* can never fail.
*/
WSAStartup(wVersionRequested, &wsaData);
#ifdef STATIC_BUILD
/*
* If we are in a statically linked executable, then we need to explicitly
* initialize the Windows function tables here since DllMain() will not be
* invoked.
*/
TclWinInit(GetModuleHandle(NULL));
#endif
/*
* Fill available functions depending on windows version
*/
hInstance = LoadLibraryW(L"kernel32");
if (hInstance != NULL) {
_tclWinProcs.cancelSynchronousIo =
(BOOL (WINAPI *)(HANDLE)) GetProcAddress(hInstance,
"CancelSynchronousIo");
}
}
/*
*-------------------------------------------------------------------------
*
* TclpInitLibraryPath --
*
|
| ︙ |
Changes to win/tclWinInt.h.
| ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | + + + + + + + + + |
struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
void *ebp;
void *esp;
int status;
} TCLEXCEPTION_REGISTRATION;
#endif
/*
* Windows version dependend functions
*/
typedef struct TclWinProcs {
BOOL (WINAPI *cancelSynchronousIo)(HANDLE);
} TclWinProcs;
MODULE_SCOPE TclWinProcs *tclWinProcs;
/*
* Some versions of Borland C have a define for the OSVERSIONINFO for
* Win32s and for NT, but not for Windows 95.
* Define VER_PLATFORM_WIN32_CE for those without newer headers.
*/
#ifndef VER_PLATFORM_WIN32_WINDOWS
|
| ︙ |
Changes to win/tclWinPipe.c.
| ︙ | |||
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - |
* outdated Win 95 systems. If this can be confirmed, much code can be
* deleted.
*/
/*
* This structure describes per-instance data for a pipe based channel.
*/
typedef struct PipeThreadInfo {
HANDLE evControl; /* Auto-reset event used by the main thread to
* signal when the pipe thread should attempt
* to do read/write operation. Additionally
* used as signal to stop (state set to -1) */
volatile LONG state; /* Indicates current state of the thread */
ClientData clientData; /* Referenced data of the main thread */
} PipeThreadInfo;
/* If pipe-workers will use some tcl subsystem, we can use ckalloc without
* more overhead for finalize thread (should be executed anyway)
*
* #define _PTI_USE_CKALLOC 1
*/
/*
* State of the pipe-worker.
*
* State PTI_STATE_STOP possible from idle state only, worker owns TI structure.
* Otherwise PTI_STATE_END used (main thread hold ownership of the TI).
*/
#define PTI_STATE_IDLE 0 /* idle or not yet initialzed */
#define PTI_STATE_WORK 1 /* in work */
#define PTI_STATE_STOP 2 /* thread should stop work (owns TI structure) */
#define PTI_STATE_END 4 /* thread should stop work (worker is busy) */
#define PTI_STATE_DOWN 8 /* worker is down */
PipeThreadInfo *
PipeThreadCreateTI(
PipeThreadInfo **pipeTIPtr,
ClientData clientData)
{
PipeThreadInfo *pipeTI;
#ifndef _PTI_USE_CKALLOC
pipeTI = malloc(sizeof(PipeThreadInfo));
#else
pipeTI = ckalloc(sizeof(PipeThreadInfo));
#endif
pipeTI->evControl = CreateEvent(NULL, FALSE, FALSE, NULL);
pipeTI->state = PTI_STATE_IDLE;
pipeTI->clientData = clientData;
return (*pipeTIPtr = pipeTI);
}
int
PipeThreadWaitForSignal(
PipeThreadInfo **pipeTIPtr)
{
PipeThreadInfo *pipeTI = *pipeTIPtr;
LONG state;
DWORD waitResult;
if (!pipeTI) {
return 0;
}
/*
* Wait for the main thread to signal before attempting to do the work.
*/
/* reset work state of thread (idle/waiting) */
if ((state = InterlockedCompareExchange(&pipeTI->state,
PTI_STATE_IDLE, PTI_STATE_WORK)) & (PTI_STATE_STOP|PTI_STATE_END)) {
/* end of work, check the owner of structure */
goto end;
}
/* entering wait */
waitResult = WaitForSingleObject(pipeTI->evControl, INFINITE);
if (waitResult != WAIT_OBJECT_0) {
/*
* The control event was not signaled, so end of work (unexpected
* behaviour, main thread can be dead?).
*/
goto end;
}
/* try to set work state of thread */
if ((state = InterlockedCompareExchange(&pipeTI->state,
PTI_STATE_WORK, PTI_STATE_IDLE)) & (PTI_STATE_STOP|PTI_STATE_END)) {
/* end of work */
goto end;
}
/* signaled to work */
return 1;
end:
/* end of work, check the owner of the TI structure */
if (state != PTI_STATE_STOP) {
*pipeTIPtr = NULL;
}
return 0;
}
static inline void
PipeThreadSignal(
PipeThreadInfo **pipeTIPtr)
{
PipeThreadInfo *pipeTI = *pipeTIPtr;
if (pipeTI) {
SetEvent(pipeTI->evControl);
}
}
int
PipeThreadStopSignal(
PipeThreadInfo **pipeTIPtr)
{
PipeThreadInfo *pipeTI = *pipeTIPtr;
HANDLE evControl;
int state;
if (!pipeTI) {
return 1;
}
evControl = pipeTI->evControl;
switch (
(state = InterlockedCompareExchange(&pipeTI->state,
PTI_STATE_STOP, PTI_STATE_IDLE))
) {
case PTI_STATE_IDLE:
/* Thread was idle/waiting, notify it goes teardown */
SetEvent(evControl);
*pipeTIPtr = NULL;
case PTI_STATE_DOWN:
return 1;
default:
/*
* Thread works currently, we should try to end it, own the TI structure
* (because of possible sharing the joint structures with thread)
*/
InterlockedExchange(&pipeTI->state, PTI_STATE_END);
break;
}
return 0;
}
void
PipeThreadStop(
PipeThreadInfo **pipeTIPtr,
HANDLE hThread)
{
PipeThreadInfo *pipeTI = *pipeTIPtr;
HANDLE evControl;
int state;
if (!pipeTI) {
return;
}
pipeTI = *pipeTIPtr;
evControl = pipeTI->evControl;
/*
* Try to sane stop the pipe worker, corresponding its current state
*/
switch (
(state = InterlockedCompareExchange(&pipeTI->state,
PTI_STATE_STOP, PTI_STATE_IDLE))
) {
case PTI_STATE_IDLE:
/* Thread was idle/waiting, notify it goes teardown */
SetEvent(evControl);
/* we don't need to wait for it at all, thread frees himself (owns the TI structure) */
pipeTI = NULL;
break;
case PTI_STATE_STOP:
/* already stopped, thread frees himself (owns the TI structure) */
pipeTI = NULL;
break;
case PTI_STATE_DOWN:
/* Thread already down (?), do nothing */
/* we don't need to wait for it, but we should free pipeTI */
hThread = NULL;
break;
/* case PTI_STATE_WORK: */
default:
/*
* Thread works currently, we should try to end it, own the TI structure
* (because of possible sharing the joint structures with thread)
*/
InterlockedExchange(&pipeTI->state, PTI_STATE_END);
break;
}
if (pipeTI && hThread) {
DWORD exitCode;
/*
* The thread may already have closed on its own. Check its exit
* code.
*/
GetExitCodeThread(hThread, &exitCode);
if (exitCode == STILL_ACTIVE) {
/*
* Set the stop event so that if the pipe thread is blocked
* somewhere, it may hereafter sane exit cleanly.
*/
SetEvent(evControl);
/*
* Cancel all sync-IO of this thread (may be blocked there).
*/
if (tclWinProcs->cancelSynchronousIo) {
tclWinProcs->cancelSynchronousIo(hThread);
}
/*
* Wait at most 20 milliseconds for the reader thread to
* close (regarding TIP#398-fast-exit).
*/
/* if we want TIP#398-fast-exit. */
if (WaitForSingleObject(hThread,
TclInExit() ? 0 : 20) == WAIT_TIMEOUT) {
/*
* The thread must be blocked waiting for the pipe to
* become readable in ReadFile(). There isn't a clean way
* to exit the thread from this condition. We should
* terminate the child process instead to get the reader
* thread to fall out of ReadFile with a FALSE. (below) is
* not the correct way to do this, but will stay here
* until a better solution is found.
*
* Note that we need to guard against terminating the
* thread while it is in the middle of Tcl_ThreadAlert
* because it won't be able to release the notifier lock.
*
* Also note that terminating threads during their initialization or teardown phase
* may result in ntdll.dll's LoaderLock to remain locked indefinitely.
* This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock.
* LdrpInitializeThread() is executed within new threads to perform
* initialization and to execute DllMain() of all loaded dlls.
* As a result, all new threads are deadlocked in their initialization phase and never execute,
* even though CreateThread() reports successful thread creation.
* This results in a very weird process-wide behavior, which is extremely hard to debug.
*
* THREADS SHOULD NEVER BE TERMINATED. Period.
*
* But for now, check if thread is exiting, and if so, let it die peacefully.
*/
if ( pipeTI->state != PTI_STATE_DOWN
&& WaitForSingleObject(hThread,
TclInExit() ? 0 : 5000) != WAIT_OBJECT_0
) {
Tcl_MutexLock(&pipeMutex);
/* BUG: this leaks memory */
if (!TerminateThread(hThread, 0)) {
/* terminate fails, just give thread a chance to exit */
if (InterlockedExchange(&pipeTI->state,
PTI_STATE_STOP) != PTI_STATE_DOWN) {
pipeTI = NULL;
}
};
Tcl_MutexUnlock(&pipeMutex);
}
}
}
}
*pipeTIPtr = NULL;
if (pipeTI) {
CloseHandle(pipeTI->evControl);
#ifndef _PTI_USE_CKALLOC
free(pipeTI);
#else
ckfree(pipeTI);
#endif
}
}
void
PipeThreadExit(
PipeThreadInfo **pipeTIPtr)
{
LONG state;
PipeThreadInfo *pipeTI = *pipeTIPtr;
/*
* If state of thread was set to stop (exactly), we can sane free its info
* structure, otherwise it is shared with main thread, so main thread will
* own it.
*/
if (!pipeTI) {
return;
}
*pipeTIPtr = NULL;
if ((state = InterlockedExchange(&pipeTI->state,
PTI_STATE_DOWN)) == PTI_STATE_STOP) {
CloseHandle(pipeTI->evControl);
#ifndef _PTI_USE_CKALLOC
free(pipeTI);
#else
ckfree(pipeTI);
/* be sure all subsystems used are finalized */
Tcl_FinalizeThread();
#endif
}
}
typedef struct PipeInfo {
struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */
Tcl_Channel channel; /* Pointer to channel structure. */
int validMask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which operations are valid on the file. */
int watchMask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which events should be reported. */
int flags; /* State flags, see above for a list. */
TclFile readFile; /* Output from pipe. */
TclFile writeFile; /* Input from pipe. */
TclFile errorFile; /* Error output from pipe. */
int numPids; /* Number of processes attached to pipe. */
Tcl_Pid *pidPtr; /* Pids of attached processes. */
Tcl_ThreadId threadId; /* Thread to which events should be reported.
* This value is used by the reader/writer
* threads. */
PipeThreadInfo *writeTI; /* Thread info of writer and reader, this */
PipeThreadInfo *readTI; /* structure owned by corresponding thread. */
HANDLE writeThread; /* Handle to writer thread. */
HANDLE readThread; /* Handle to reader thread. */
HANDLE writable; /* Manual-reset event to signal when the
* writer thread has finished waiting for the
* current buffer to be written. */
HANDLE readable; /* Manual-reset event to signal when the
* reader thread has finished waiting for
* input. */
|
| ︙ | |||
1595 1596 1597 1598 1599 1600 1601 | 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 | - - - + + - - - - + + + + + |
if (readFile != NULL) {
/*
* Start the background reader thread.
*/
infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
|
| ︙ | |||
1801 1802 1803 1804 1805 1806 1807 | 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 | - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + - + - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |
int flags) /* Flags that indicate which side to close. */
{
PipeInfo *pipePtr = (PipeInfo *) instanceData;
Tcl_Channel errChan;
int errorCode, result;
PipeInfo *infoPtr, **nextPtrPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
|
| ︙ | |||
2198 2199 2200 2201 2202 2203 2204 | 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 | - + | } infoPtr->writeBufLen = toWrite; infoPtr->writeBuf = ckalloc(toWrite); } memcpy(infoPtr->writeBuf, buf, (size_t) toWrite); infoPtr->toWrite = toWrite; ResetEvent(infoPtr->writable); |
| ︙ | |||
2782 2783 2784 2785 2786 2787 2788 | 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 | - + | } /* * There wasn't any data available, so reset the thread and try again. */ ResetEvent(infoPtr->readable); |
| ︙ | |||
2810 2811 2812 2813 2814 2815 2816 | 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 | + - - + + - - - + - - - + + + - + - - + - - - - - - + + |
*----------------------------------------------------------------------
*/
static DWORD WINAPI
PipeReaderThread(
LPVOID arg)
{
PipeThreadInfo *pipeTI = (PipeThreadInfo *)arg;
|
| ︙ | |||
2883 2884 2885 2886 2887 2888 2889 | 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 | - |
done = 1;
} else if (err == ERROR_INVALID_HANDLE) {
break;
}
}
}
|
| ︙ | |||
2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 | 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 | + + + + + + |
*/
Tcl_ThreadAlert(infoPtr->threadId);
}
Tcl_MutexUnlock(&pipeMutex);
}
/*
* If state of thread was set to stop, we can sane free info structure,
* otherwise it is shared with main thread, so main thread will own it
*/
PipeThreadExit(&pipeTI);
return 0;
}
/*
*----------------------------------------------------------------------
*
* PipeWriterThread --
|
| ︙ | |||
2933 2934 2935 2936 2937 2938 2939 | 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 | + - - + + - - - - - - + - - - + + - - + - - - + - - - + + - + + + + |
*----------------------------------------------------------------------
*/
static DWORD WINAPI
PipeWriterThread(
LPVOID arg)
{
PipeThreadInfo *pipeTI = (PipeThreadInfo *)arg;
|
| ︙ | |||
2987 2988 2989 2990 2991 2992 2993 | 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 | - + + + + + + + | } /* * Signal the main thread by signalling the writable event and then * waking up the notifier thread. */ |
| ︙ |
Changes to win/tclWinSerial.c.
| ︙ | |||
90 91 92 93 94 95 96 97 98 99 100 101 102 | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | + - + + |
Tcl_ThreadId threadId; /* Thread to which events should be reported.
* This value is used by the reader/writer
* threads. */
OVERLAPPED osRead; /* OVERLAPPED structure for read operations. */
OVERLAPPED osWrite; /* OVERLAPPED structure for write operations */
HANDLE writeThread; /* Handle to writer thread. */
int writeThreadExiting; /* Boolean indicating that thread is exiting. */
CRITICAL_SECTION csWrite; /* Writer thread synchronisation. */
HANDLE evWritable; /* Manual-reset event to signal when the
* writer thread has finished waiting for the
* current buffer to be written. */
HANDLE evStartWriter; /* Auto-reset event used by the main thread to
* signal when the writer thread should
|
| ︙ | |||
639 640 641 642 643 644 645 646 647 | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | + + + + + + + + + + + + + + + + - + - - - + + - - + + |
if (WaitForSingleObject(serialPtr->writeThread,
20) == WAIT_TIMEOUT) {
/*
* Forcibly terminate the background thread as a last resort.
* Note that we need to guard against terminating the thread
* while it is in the middle of Tcl_ThreadAlert because it
* won't be able to release the notifier lock.
*
* Also note that terminating threads during their initialization or teardown phase
* may result in ntdll.dll's LoaderLock to remain locked indefinitely.
* This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock.
* LdrpInitializeThread() is executed within new threads to perform
* initialization and to execute DllMain() of all loaded dlls.
* As a result, all new threads are deadlocked in their initialization phase and never execute,
* even though CreateThread() reports successful thread creation.
* This results in a very weird process-wide behavior, which is extremely hard to debug.
*
* THREADS SHOULD NEVER BE TERMINATED. Period.
*
* But for now, check if thread is exiting, and if so, let it die peacefully.
*/
if ( !serialPtr->writeThreadExiting
|| WaitForSingleObject(serialPtr->writeThread, 5000) != WAIT_OBJECT_0
) {
|
| ︙ | |||
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 | 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 | + + + + + |
{
SerialInfo *infoPtr = (SerialInfo *)arg;
DWORD bytesWritten, toWrite, waitResult;
char *buf;
OVERLAPPED myWrite; /* Have an own OVERLAPPED in this thread. */
HANDLE wEvents[2];
/*
* Notify TclWinOpenSerialChannel() that this thread is initialized
*/
SignalObjectAndWait(infoPtr->evStartWriter, infoPtr->evStopWriter, INFINITE, FALSE);
/*
* The stop event takes precedence by being first in the list.
*/
wEvents[0] = infoPtr->evStopWriter;
wEvents[1] = infoPtr->evStartWriter;
|
| ︙ | |||
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 | 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 | + + + + + + |
*/
Tcl_ThreadAlert(infoPtr->threadId);
}
Tcl_MutexUnlock(&serialMutex);
}
/*
* Inform caller that this thread should not be terminated, since it is about to exit.
* See comment in SerialCloseProc() for reasons.
*/
infoPtr->writeThreadExiting = TRUE;
return 0;
}
/*
*----------------------------------------------------------------------
*
* TclWinSerialOpen --
|
| ︙ | |||
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 | + + + + + |
* Initially the channel is writable and the writeThread is idle.
*/
infoPtr->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
infoPtr->evWritable = CreateEvent(NULL, TRUE, TRUE, NULL);
infoPtr->evStartWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
infoPtr->evStopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
infoPtr->writeThreadExiting = FALSE;
infoPtr->writeThread = CreateThread(NULL, 256, SerialWriterThread,
infoPtr, 0, &id);
/* Wait for thread to initialize (using evStartWriter) */
WaitForSingleObject(infoPtr->evStartWriter, 5000);
/* Wake-up it to signal we've get an event */
SetEvent(infoPtr->evStopWriter);
}
/*
* Files have default translation of AUTO and ^Z eof char, which means
* that a ^Z will be accepted as EOF when reading.
*/
|
| ︙ |