os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinNotify.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/* 
sl@0
     2
 * tclWinNotify.c --
sl@0
     3
 *
sl@0
     4
 *	This file contains Windows-specific procedures for the notifier,
sl@0
     5
 *	which is the lowest-level part of the Tcl event loop.  This file
sl@0
     6
 *	works together with ../generic/tclNotify.c.
sl@0
     7
 *
sl@0
     8
 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
sl@0
     9
 *
sl@0
    10
 * See the file "license.terms" for information on usage and redistribution
sl@0
    11
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    12
 *
sl@0
    13
 * RCS: @(#) $Id: tclWinNotify.c,v 1.11.2.1 2003/03/21 03:24:09 dgp Exp $
sl@0
    14
 */
sl@0
    15
sl@0
    16
#include "tclWinInt.h"
sl@0
    17
sl@0
    18
/*
sl@0
    19
 * The follwing static indicates whether this module has been initialized.
sl@0
    20
 */
sl@0
    21
sl@0
    22
#define INTERVAL_TIMER 1	/* Handle of interval timer. */
sl@0
    23
sl@0
    24
#define WM_WAKEUP WM_USER	/* Message that is send by
sl@0
    25
				 * Tcl_AlertNotifier. */
sl@0
    26
/*
sl@0
    27
 * The following static structure contains the state information for the
sl@0
    28
 * Windows implementation of the Tcl notifier.  One of these structures
sl@0
    29
 * is created for each thread that is using the notifier.  
sl@0
    30
 */
sl@0
    31
sl@0
    32
typedef struct ThreadSpecificData {
sl@0
    33
    CRITICAL_SECTION crit;	/* Monitor for this notifier. */
sl@0
    34
    DWORD thread;		/* Identifier for thread associated with this
sl@0
    35
				 * notifier. */
sl@0
    36
    HANDLE event;		/* Event object used to wake up the notifier
sl@0
    37
				 * thread. */
sl@0
    38
    int pending;		/* Alert message pending, this field is
sl@0
    39
				 * locked by the notifierMutex. */
sl@0
    40
    HWND hwnd;			/* Messaging window. */
sl@0
    41
    int timeout;		/* Current timeout value. */
sl@0
    42
    int timerActive;		/* 1 if interval timer is running. */
sl@0
    43
} ThreadSpecificData;
sl@0
    44
sl@0
    45
static Tcl_ThreadDataKey dataKey;
sl@0
    46
sl@0
    47
extern TclStubs tclStubs;
sl@0
    48
extern Tcl_NotifierProcs tclOriginalNotifier;
sl@0
    49
sl@0
    50
/*
sl@0
    51
 * The following static indicates the number of threads that have
sl@0
    52
 * initialized notifiers.  It controls the lifetime of the TclNotifier
sl@0
    53
 * window class.
sl@0
    54
 *
sl@0
    55
 * You must hold the notifierMutex lock before accessing this variable.
sl@0
    56
 */
sl@0
    57
sl@0
    58
static int notifierCount = 0;
sl@0
    59
TCL_DECLARE_MUTEX(notifierMutex)
sl@0
    60
sl@0
    61
/*
sl@0
    62
 * Static routines defined in this file.
sl@0
    63
 */
sl@0
    64
sl@0
    65
static LRESULT CALLBACK	NotifierProc(HWND hwnd, UINT message,
sl@0
    66
			    WPARAM wParam, LPARAM lParam);
sl@0
    67
sl@0
    68

sl@0
    69
/*
sl@0
    70
 *----------------------------------------------------------------------
sl@0
    71
 *
sl@0
    72
 * Tcl_InitNotifier --
sl@0
    73
 *
sl@0
    74
 *	Initializes the platform specific notifier state.
sl@0
    75
 *
sl@0
    76
 * Results:
sl@0
    77
 *	Returns a handle to the notifier state for this thread..
sl@0
    78
 *
sl@0
    79
 * Side effects:
sl@0
    80
 *	None.
sl@0
    81
 *
sl@0
    82
 *----------------------------------------------------------------------
sl@0
    83
 */
sl@0
    84
sl@0
    85
ClientData
sl@0
    86
Tcl_InitNotifier()
sl@0
    87
{
sl@0
    88
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
    89
    WNDCLASS class;
sl@0
    90
sl@0
    91
    /*
sl@0
    92
     * Register Notifier window class if this is the first thread to
sl@0
    93
     * use this module.
sl@0
    94
     */
sl@0
    95
sl@0
    96
    Tcl_MutexLock(&notifierMutex);
sl@0
    97
    if (notifierCount == 0) {
sl@0
    98
	class.style = 0;
sl@0
    99
	class.cbClsExtra = 0;
sl@0
   100
	class.cbWndExtra = 0;
sl@0
   101
	class.hInstance = TclWinGetTclInstance();
sl@0
   102
	class.hbrBackground = NULL;
sl@0
   103
	class.lpszMenuName = NULL;
sl@0
   104
	class.lpszClassName = "TclNotifier";
sl@0
   105
	class.lpfnWndProc = NotifierProc;
sl@0
   106
	class.hIcon = NULL;
sl@0
   107
	class.hCursor = NULL;
sl@0
   108
sl@0
   109
	if (!RegisterClassA(&class)) {
sl@0
   110
	    panic("Unable to register TclNotifier window class");
sl@0
   111
	}
sl@0
   112
    }
sl@0
   113
    notifierCount++;
sl@0
   114
    Tcl_MutexUnlock(&notifierMutex);
sl@0
   115
sl@0
   116
    tsdPtr->pending = 0;
sl@0
   117
    tsdPtr->timerActive = 0;
sl@0
   118
sl@0
   119
    InitializeCriticalSection(&tsdPtr->crit);
sl@0
   120
sl@0
   121
    tsdPtr->hwnd = NULL;
sl@0
   122
    tsdPtr->thread = GetCurrentThreadId();
sl@0
   123
    tsdPtr->event = CreateEvent(NULL, TRUE /* manual */,
sl@0
   124
	    FALSE /* !signaled */, NULL);
sl@0
   125
sl@0
   126
    return (ClientData) tsdPtr;
sl@0
   127
}
sl@0
   128

sl@0
   129
/*
sl@0
   130
 *----------------------------------------------------------------------
sl@0
   131
 *
sl@0
   132
 * Tcl_FinalizeNotifier --
sl@0
   133
 *
sl@0
   134
 *	This function is called to cleanup the notifier state before
sl@0
   135
 *	a thread is terminated.
sl@0
   136
 *
sl@0
   137
 * Results:
sl@0
   138
 *	None.
sl@0
   139
 *
sl@0
   140
 * Side effects:
sl@0
   141
 *	May dispose of the notifier window and class.
sl@0
   142
 *
sl@0
   143
 *----------------------------------------------------------------------
sl@0
   144
 */
sl@0
   145
sl@0
   146
void
sl@0
   147
Tcl_FinalizeNotifier(clientData)
sl@0
   148
    ClientData clientData;	/* Pointer to notifier data. */
sl@0
   149
{
sl@0
   150
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
sl@0
   151
sl@0
   152
    /*
sl@0
   153
     * Only finalize the notifier if a notifier was installed in the
sl@0
   154
     * current thread; there is a route in which this is not
sl@0
   155
     * guaranteed to be true (when tclWin32Dll.c:DllMain() is called
sl@0
   156
     * with the flag DLL_PROCESS_DETACH by the OS, which could be
sl@0
   157
     * doing so from a thread that's never previously been involved
sl@0
   158
     * with Tcl, e.g. the task manager) so this check is important.
sl@0
   159
     *
sl@0
   160
     * Fixes Bug #217982 reported by Hugh Vu and Gene Leache.
sl@0
   161
     */
sl@0
   162
    if (tsdPtr == NULL) {
sl@0
   163
	return;
sl@0
   164
    }
sl@0
   165
sl@0
   166
    DeleteCriticalSection(&tsdPtr->crit);
sl@0
   167
    CloseHandle(tsdPtr->event);
sl@0
   168
sl@0
   169
    /*
sl@0
   170
     * Clean up the timer and messaging window for this thread.
sl@0
   171
     */
sl@0
   172
sl@0
   173
    if (tsdPtr->hwnd) {
sl@0
   174
	KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
sl@0
   175
	DestroyWindow(tsdPtr->hwnd);
sl@0
   176
    }
sl@0
   177
sl@0
   178
    /*
sl@0
   179
     * If this is the last thread to use the notifier, unregister
sl@0
   180
     * the notifier window class.
sl@0
   181
     */
sl@0
   182
sl@0
   183
    Tcl_MutexLock(&notifierMutex);
sl@0
   184
    notifierCount--;
sl@0
   185
    if (notifierCount == 0) {
sl@0
   186
	UnregisterClassA("TclNotifier", TclWinGetTclInstance());
sl@0
   187
    }
sl@0
   188
    Tcl_MutexUnlock(&notifierMutex);
sl@0
   189
}
sl@0
   190

sl@0
   191
/*
sl@0
   192
 *----------------------------------------------------------------------
sl@0
   193
 *
sl@0
   194
 * Tcl_AlertNotifier --
sl@0
   195
 *
sl@0
   196
 *	Wake up the specified notifier from any thread. This routine
sl@0
   197
 *	is called by the platform independent notifier code whenever
sl@0
   198
 *	the Tcl_ThreadAlert routine is called.  This routine is
sl@0
   199
 *	guaranteed not to be called on a given notifier after
sl@0
   200
 *	Tcl_FinalizeNotifier is called for that notifier.  This routine
sl@0
   201
 *	is typically called from a thread other than the notifier's
sl@0
   202
 *	thread.
sl@0
   203
 *
sl@0
   204
 * Results:
sl@0
   205
 *	None.
sl@0
   206
 *
sl@0
   207
 * Side effects:
sl@0
   208
 *	Sends a message to the messaging window for the notifier
sl@0
   209
 *	if there isn't already one pending.
sl@0
   210
 *
sl@0
   211
 *----------------------------------------------------------------------
sl@0
   212
 */
sl@0
   213
sl@0
   214
void
sl@0
   215
Tcl_AlertNotifier(clientData)
sl@0
   216
    ClientData clientData;	/* Pointer to thread data. */
sl@0
   217
{
sl@0
   218
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
sl@0
   219
sl@0
   220
    /*
sl@0
   221
     * Note that we do not need to lock around access to the hwnd
sl@0
   222
     * because the race condition has no effect since any race condition
sl@0
   223
     * implies that the notifier thread is already awake.
sl@0
   224
     */
sl@0
   225
sl@0
   226
    if (tsdPtr->hwnd) {
sl@0
   227
	/*
sl@0
   228
	 * We do need to lock around access to the pending flag.
sl@0
   229
	 */
sl@0
   230
sl@0
   231
	EnterCriticalSection(&tsdPtr->crit);
sl@0
   232
	if (!tsdPtr->pending) {
sl@0
   233
	    PostMessage(tsdPtr->hwnd, WM_WAKEUP, 0, 0);
sl@0
   234
	}
sl@0
   235
	tsdPtr->pending = 1;
sl@0
   236
	LeaveCriticalSection(&tsdPtr->crit);
sl@0
   237
    } else {
sl@0
   238
	SetEvent(tsdPtr->event);
sl@0
   239
    }
sl@0
   240
}
sl@0
   241

sl@0
   242
/*
sl@0
   243
 *----------------------------------------------------------------------
sl@0
   244
 *
sl@0
   245
 * Tcl_SetTimer --
sl@0
   246
 *
sl@0
   247
 *	This procedure sets the current notifier timer value.  The
sl@0
   248
 *	notifier will ensure that Tcl_ServiceAll() is called after
sl@0
   249
 *	the specified interval, even if no events have occurred.
sl@0
   250
 *
sl@0
   251
 * Results:
sl@0
   252
 *	None.
sl@0
   253
 *
sl@0
   254
 * Side effects:
sl@0
   255
 *	Replaces any previous timer.
sl@0
   256
 *
sl@0
   257
 *----------------------------------------------------------------------
sl@0
   258
 */
sl@0
   259
sl@0
   260
void
sl@0
   261
Tcl_SetTimer(
sl@0
   262
    Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
sl@0
   263
{
sl@0
   264
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   265
    UINT timeout;
sl@0
   266
sl@0
   267
    /*
sl@0
   268
     * Allow the notifier to be hooked.  This may not make sense
sl@0
   269
     * on Windows, but mirrors the UNIX hook.
sl@0
   270
     */
sl@0
   271
sl@0
   272
    if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
sl@0
   273
	tclStubs.tcl_SetTimer(timePtr);
sl@0
   274
	return;
sl@0
   275
    }
sl@0
   276
sl@0
   277
    /*
sl@0
   278
     * We only need to set up an interval timer if we're being called
sl@0
   279
     * from an external event loop.  If we don't have a window handle
sl@0
   280
     * then we just return immediately and let Tcl_WaitForEvent handle
sl@0
   281
     * timeouts.
sl@0
   282
     */
sl@0
   283
sl@0
   284
    if (!tsdPtr->hwnd) {
sl@0
   285
	return;
sl@0
   286
    }
sl@0
   287
sl@0
   288
    if (!timePtr) {
sl@0
   289
	timeout = 0;
sl@0
   290
    } else {
sl@0
   291
	/*
sl@0
   292
	 * Make sure we pass a non-zero value into the timeout argument.
sl@0
   293
	 * Windows seems to get confused by zero length timers.
sl@0
   294
	 */
sl@0
   295
sl@0
   296
	timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
sl@0
   297
	if (timeout == 0) {
sl@0
   298
	    timeout = 1;
sl@0
   299
	}
sl@0
   300
    }
sl@0
   301
    tsdPtr->timeout = timeout;
sl@0
   302
    if (timeout != 0) {
sl@0
   303
	tsdPtr->timerActive = 1;
sl@0
   304
	SetTimer(tsdPtr->hwnd, INTERVAL_TIMER,
sl@0
   305
		    (unsigned long) tsdPtr->timeout, NULL);
sl@0
   306
    } else {
sl@0
   307
	tsdPtr->timerActive = 0;
sl@0
   308
	KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
sl@0
   309
    }
sl@0
   310
}
sl@0
   311

sl@0
   312
/*
sl@0
   313
 *----------------------------------------------------------------------
sl@0
   314
 *
sl@0
   315
 * Tcl_ServiceModeHook --
sl@0
   316
 *
sl@0
   317
 *	This function is invoked whenever the service mode changes.
sl@0
   318
 *
sl@0
   319
 * Results:
sl@0
   320
 *	None.
sl@0
   321
 *
sl@0
   322
 * Side effects:
sl@0
   323
 *	If this is the first time the notifier is set into
sl@0
   324
 *	TCL_SERVICE_ALL, then the communication window is created.
sl@0
   325
 *
sl@0
   326
 *----------------------------------------------------------------------
sl@0
   327
 */
sl@0
   328
sl@0
   329
void
sl@0
   330
Tcl_ServiceModeHook(mode)
sl@0
   331
    int mode;			/* Either TCL_SERVICE_ALL, or
sl@0
   332
				 * TCL_SERVICE_NONE. */
sl@0
   333
{
sl@0
   334
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   335
sl@0
   336
    /*
sl@0
   337
     * If this is the first time that the notifier has been used from a
sl@0
   338
     * modal loop, then create a communication window.  Note that after
sl@0
   339
     * this point, the application needs to service events in a timely
sl@0
   340
     * fashion or Windows will hang waiting for the window to respond
sl@0
   341
     * to synchronous system messages.  At some point, we may want to
sl@0
   342
     * consider destroying the window if we leave the modal loop, but
sl@0
   343
     * for now we'll leave it around.
sl@0
   344
     */
sl@0
   345
sl@0
   346
    if (mode == TCL_SERVICE_ALL && !tsdPtr->hwnd) {
sl@0
   347
	tsdPtr->hwnd = CreateWindowA("TclNotifier", "TclNotifier", WS_TILED,
sl@0
   348
		0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL);
sl@0
   349
	/*
sl@0
   350
	 * Send an initial message to the window to ensure that we wake up the
sl@0
   351
	 * notifier once we get into the modal loop.  This will force the
sl@0
   352
	 * notifier to recompute the timeout value and schedule a timer
sl@0
   353
	 * if one is needed.
sl@0
   354
	 */
sl@0
   355
sl@0
   356
	Tcl_AlertNotifier((ClientData)tsdPtr);
sl@0
   357
    }
sl@0
   358
}
sl@0
   359

sl@0
   360
/*
sl@0
   361
 *----------------------------------------------------------------------
sl@0
   362
 *
sl@0
   363
 * NotifierProc --
sl@0
   364
 *
sl@0
   365
 *	This procedure is invoked by Windows to process events on
sl@0
   366
 *	the notifier window.  Messages will be sent to this window
sl@0
   367
 *	in response to external timer events or calls to
sl@0
   368
 *	TclpAlertTsdPtr->
sl@0
   369
 *
sl@0
   370
 * Results:
sl@0
   371
 *	A standard windows result.
sl@0
   372
 *
sl@0
   373
 * Side effects:
sl@0
   374
 *	Services any pending events.
sl@0
   375
 *
sl@0
   376
 *----------------------------------------------------------------------
sl@0
   377
 */
sl@0
   378
sl@0
   379
static LRESULT CALLBACK
sl@0
   380
NotifierProc(
sl@0
   381
    HWND hwnd,
sl@0
   382
    UINT message,
sl@0
   383
    WPARAM wParam,
sl@0
   384
    LPARAM lParam)
sl@0
   385
{
sl@0
   386
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   387
sl@0
   388
    if (message == WM_WAKEUP) {
sl@0
   389
	EnterCriticalSection(&tsdPtr->crit);
sl@0
   390
	tsdPtr->pending = 0;
sl@0
   391
	LeaveCriticalSection(&tsdPtr->crit);
sl@0
   392
    } else if (message != WM_TIMER) {
sl@0
   393
	return DefWindowProc(hwnd, message, wParam, lParam);
sl@0
   394
    }
sl@0
   395
	
sl@0
   396
    /*
sl@0
   397
     * Process all of the runnable events.
sl@0
   398
     */
sl@0
   399
sl@0
   400
    Tcl_ServiceAll();
sl@0
   401
    return 0;
sl@0
   402
}
sl@0
   403

sl@0
   404
/*
sl@0
   405
 *----------------------------------------------------------------------
sl@0
   406
 *
sl@0
   407
 * Tcl_WaitForEvent --
sl@0
   408
 *
sl@0
   409
 *	This function is called by Tcl_DoOneEvent to wait for new
sl@0
   410
 *	events on the message queue.  If the block time is 0, then
sl@0
   411
 *	Tcl_WaitForEvent just polls the event queue without blocking.
sl@0
   412
 *
sl@0
   413
 * Results:
sl@0
   414
 *	Returns -1 if a WM_QUIT message is detected, returns 1 if
sl@0
   415
 *	a message was dispatched, otherwise returns 0.
sl@0
   416
 *
sl@0
   417
 * Side effects:
sl@0
   418
 *	Dispatches a message to a window procedure, which could do
sl@0
   419
 *	anything.
sl@0
   420
 *
sl@0
   421
 *----------------------------------------------------------------------
sl@0
   422
 */
sl@0
   423
sl@0
   424
int
sl@0
   425
Tcl_WaitForEvent(
sl@0
   426
    Tcl_Time *timePtr)		/* Maximum block time, or NULL. */
sl@0
   427
{
sl@0
   428
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   429
    MSG msg;
sl@0
   430
    DWORD timeout, result;
sl@0
   431
    int status;
sl@0
   432
sl@0
   433
    /*
sl@0
   434
     * Allow the notifier to be hooked.  This may not make
sl@0
   435
     * sense on windows, but mirrors the UNIX hook.
sl@0
   436
     */
sl@0
   437
sl@0
   438
    if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
sl@0
   439
	return tclStubs.tcl_WaitForEvent(timePtr);
sl@0
   440
    }
sl@0
   441
sl@0
   442
    /*
sl@0
   443
     * Compute the timeout in milliseconds.
sl@0
   444
     */
sl@0
   445
sl@0
   446
    if (timePtr) {
sl@0
   447
	timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
sl@0
   448
    } else {
sl@0
   449
	timeout = INFINITE;
sl@0
   450
    }
sl@0
   451
sl@0
   452
    /*
sl@0
   453
     * Check to see if there are any messages in the queue before waiting
sl@0
   454
     * because MsgWaitForMultipleObjects will not wake up if there are events
sl@0
   455
     * currently sitting in the queue.
sl@0
   456
     */
sl@0
   457
sl@0
   458
    if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
sl@0
   459
	/*
sl@0
   460
	 * Wait for something to happen (a signal from another thread, a
sl@0
   461
	 * message, or timeout).
sl@0
   462
	 */
sl@0
   463
sl@0
   464
	result = MsgWaitForMultipleObjects(1, &tsdPtr->event, FALSE, timeout,
sl@0
   465
		QS_ALLINPUT);
sl@0
   466
    }
sl@0
   467
sl@0
   468
    /*
sl@0
   469
     * Check to see if there are any messages to process.
sl@0
   470
     */
sl@0
   471
sl@0
   472
    if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
sl@0
   473
	/*
sl@0
   474
	 * Retrieve and dispatch the first message.
sl@0
   475
	 */
sl@0
   476
sl@0
   477
	result = GetMessage(&msg, NULL, 0, 0);
sl@0
   478
	if (result == 0) {
sl@0
   479
	    /*
sl@0
   480
	     * We received a request to exit this thread (WM_QUIT), so
sl@0
   481
	     * propagate the quit message and start unwinding.
sl@0
   482
	     */
sl@0
   483
sl@0
   484
	    PostQuitMessage((int) msg.wParam);
sl@0
   485
	    status = -1;
sl@0
   486
	} else if (result == -1) {
sl@0
   487
	    /*
sl@0
   488
	     * We got an error from the system.  I have no idea why this would
sl@0
   489
	     * happen, so we'll just unwind.
sl@0
   490
	     */
sl@0
   491
sl@0
   492
	    status = -1;
sl@0
   493
	} else {
sl@0
   494
	    TranslateMessage(&msg);
sl@0
   495
	    DispatchMessage(&msg);
sl@0
   496
	    status = 1;
sl@0
   497
	}
sl@0
   498
    } else {
sl@0
   499
	status = 0;
sl@0
   500
    }
sl@0
   501
sl@0
   502
    ResetEvent(tsdPtr->event);
sl@0
   503
    return status;
sl@0
   504
}
sl@0
   505

sl@0
   506
/*
sl@0
   507
 *----------------------------------------------------------------------
sl@0
   508
 *
sl@0
   509
 * Tcl_Sleep --
sl@0
   510
 *
sl@0
   511
 *	Delay execution for the specified number of milliseconds.
sl@0
   512
 *
sl@0
   513
 * Results:
sl@0
   514
 *	None.
sl@0
   515
 *
sl@0
   516
 * Side effects:
sl@0
   517
 *	Time passes.
sl@0
   518
 *
sl@0
   519
 *----------------------------------------------------------------------
sl@0
   520
 */
sl@0
   521
sl@0
   522
void
sl@0
   523
Tcl_Sleep(ms)
sl@0
   524
    int ms;			/* Number of milliseconds to sleep. */
sl@0
   525
{
sl@0
   526
    /*
sl@0
   527
     * Simply calling 'Sleep' for the requisite number of milliseconds
sl@0
   528
     * can make the process appear to wake up early because it isn't
sl@0
   529
     * synchronized with the CPU performance counter that is used in
sl@0
   530
     * tclWinTime.c.  This behavior is probably benign, but messes
sl@0
   531
     * up some of the corner cases in the test suite.  We get around
sl@0
   532
     * this problem by repeating the 'Sleep' call as many times
sl@0
   533
     * as necessary to make the clock advance by the requisite amount.
sl@0
   534
     */
sl@0
   535
sl@0
   536
    Tcl_Time now;		/* Current wall clock time */
sl@0
   537
    Tcl_Time desired;		/* Desired wakeup time */
sl@0
   538
    DWORD sleepTime = ms;	/* Time to sleep */
sl@0
   539
sl@0
   540
    Tcl_GetTime( &now );
sl@0
   541
    desired.sec = now.sec + ( ms / 1000 );
sl@0
   542
    desired.usec = now.usec + 1000 * ( ms % 1000 );
sl@0
   543
    if ( desired.usec > 1000000 ) {
sl@0
   544
	++desired.sec;
sl@0
   545
	desired.usec -= 1000000;
sl@0
   546
    }
sl@0
   547
	
sl@0
   548
    for ( ; ; ) {
sl@0
   549
	Sleep( sleepTime );
sl@0
   550
	Tcl_GetTime( &now );
sl@0
   551
	if ( now.sec > desired.sec ) {
sl@0
   552
	    break;
sl@0
   553
	} else if ( ( now.sec == desired.sec )
sl@0
   554
	     && ( now.usec >= desired.usec ) ) {
sl@0
   555
	    break;
sl@0
   556
	}
sl@0
   557
	sleepTime = ( ( 1000 * ( desired.sec - now.sec ) )
sl@0
   558
		      + ( ( desired.usec - now.usec ) / 1000 ) );
sl@0
   559
    }
sl@0
   560
sl@0
   561
}