os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinNotify.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinNotify.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,561 @@
1.4 +/*
1.5 + * tclWinNotify.c --
1.6 + *
1.7 + * This file contains Windows-specific procedures for the notifier,
1.8 + * which is the lowest-level part of the Tcl event loop. This file
1.9 + * works together with ../generic/tclNotify.c.
1.10 + *
1.11 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.12 + *
1.13 + * See the file "license.terms" for information on usage and redistribution
1.14 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.15 + *
1.16 + * RCS: @(#) $Id: tclWinNotify.c,v 1.11.2.1 2003/03/21 03:24:09 dgp Exp $
1.17 + */
1.18 +
1.19 +#include "tclWinInt.h"
1.20 +
1.21 +/*
1.22 + * The follwing static indicates whether this module has been initialized.
1.23 + */
1.24 +
1.25 +#define INTERVAL_TIMER 1 /* Handle of interval timer. */
1.26 +
1.27 +#define WM_WAKEUP WM_USER /* Message that is send by
1.28 + * Tcl_AlertNotifier. */
1.29 +/*
1.30 + * The following static structure contains the state information for the
1.31 + * Windows implementation of the Tcl notifier. One of these structures
1.32 + * is created for each thread that is using the notifier.
1.33 + */
1.34 +
1.35 +typedef struct ThreadSpecificData {
1.36 + CRITICAL_SECTION crit; /* Monitor for this notifier. */
1.37 + DWORD thread; /* Identifier for thread associated with this
1.38 + * notifier. */
1.39 + HANDLE event; /* Event object used to wake up the notifier
1.40 + * thread. */
1.41 + int pending; /* Alert message pending, this field is
1.42 + * locked by the notifierMutex. */
1.43 + HWND hwnd; /* Messaging window. */
1.44 + int timeout; /* Current timeout value. */
1.45 + int timerActive; /* 1 if interval timer is running. */
1.46 +} ThreadSpecificData;
1.47 +
1.48 +static Tcl_ThreadDataKey dataKey;
1.49 +
1.50 +extern TclStubs tclStubs;
1.51 +extern Tcl_NotifierProcs tclOriginalNotifier;
1.52 +
1.53 +/*
1.54 + * The following static indicates the number of threads that have
1.55 + * initialized notifiers. It controls the lifetime of the TclNotifier
1.56 + * window class.
1.57 + *
1.58 + * You must hold the notifierMutex lock before accessing this variable.
1.59 + */
1.60 +
1.61 +static int notifierCount = 0;
1.62 +TCL_DECLARE_MUTEX(notifierMutex)
1.63 +
1.64 +/*
1.65 + * Static routines defined in this file.
1.66 + */
1.67 +
1.68 +static LRESULT CALLBACK NotifierProc(HWND hwnd, UINT message,
1.69 + WPARAM wParam, LPARAM lParam);
1.70 +
1.71 +
1.72 +/*
1.73 + *----------------------------------------------------------------------
1.74 + *
1.75 + * Tcl_InitNotifier --
1.76 + *
1.77 + * Initializes the platform specific notifier state.
1.78 + *
1.79 + * Results:
1.80 + * Returns a handle to the notifier state for this thread..
1.81 + *
1.82 + * Side effects:
1.83 + * None.
1.84 + *
1.85 + *----------------------------------------------------------------------
1.86 + */
1.87 +
1.88 +ClientData
1.89 +Tcl_InitNotifier()
1.90 +{
1.91 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.92 + WNDCLASS class;
1.93 +
1.94 + /*
1.95 + * Register Notifier window class if this is the first thread to
1.96 + * use this module.
1.97 + */
1.98 +
1.99 + Tcl_MutexLock(¬ifierMutex);
1.100 + if (notifierCount == 0) {
1.101 + class.style = 0;
1.102 + class.cbClsExtra = 0;
1.103 + class.cbWndExtra = 0;
1.104 + class.hInstance = TclWinGetTclInstance();
1.105 + class.hbrBackground = NULL;
1.106 + class.lpszMenuName = NULL;
1.107 + class.lpszClassName = "TclNotifier";
1.108 + class.lpfnWndProc = NotifierProc;
1.109 + class.hIcon = NULL;
1.110 + class.hCursor = NULL;
1.111 +
1.112 + if (!RegisterClassA(&class)) {
1.113 + panic("Unable to register TclNotifier window class");
1.114 + }
1.115 + }
1.116 + notifierCount++;
1.117 + Tcl_MutexUnlock(¬ifierMutex);
1.118 +
1.119 + tsdPtr->pending = 0;
1.120 + tsdPtr->timerActive = 0;
1.121 +
1.122 + InitializeCriticalSection(&tsdPtr->crit);
1.123 +
1.124 + tsdPtr->hwnd = NULL;
1.125 + tsdPtr->thread = GetCurrentThreadId();
1.126 + tsdPtr->event = CreateEvent(NULL, TRUE /* manual */,
1.127 + FALSE /* !signaled */, NULL);
1.128 +
1.129 + return (ClientData) tsdPtr;
1.130 +}
1.131 +
1.132 +/*
1.133 + *----------------------------------------------------------------------
1.134 + *
1.135 + * Tcl_FinalizeNotifier --
1.136 + *
1.137 + * This function is called to cleanup the notifier state before
1.138 + * a thread is terminated.
1.139 + *
1.140 + * Results:
1.141 + * None.
1.142 + *
1.143 + * Side effects:
1.144 + * May dispose of the notifier window and class.
1.145 + *
1.146 + *----------------------------------------------------------------------
1.147 + */
1.148 +
1.149 +void
1.150 +Tcl_FinalizeNotifier(clientData)
1.151 + ClientData clientData; /* Pointer to notifier data. */
1.152 +{
1.153 + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
1.154 +
1.155 + /*
1.156 + * Only finalize the notifier if a notifier was installed in the
1.157 + * current thread; there is a route in which this is not
1.158 + * guaranteed to be true (when tclWin32Dll.c:DllMain() is called
1.159 + * with the flag DLL_PROCESS_DETACH by the OS, which could be
1.160 + * doing so from a thread that's never previously been involved
1.161 + * with Tcl, e.g. the task manager) so this check is important.
1.162 + *
1.163 + * Fixes Bug #217982 reported by Hugh Vu and Gene Leache.
1.164 + */
1.165 + if (tsdPtr == NULL) {
1.166 + return;
1.167 + }
1.168 +
1.169 + DeleteCriticalSection(&tsdPtr->crit);
1.170 + CloseHandle(tsdPtr->event);
1.171 +
1.172 + /*
1.173 + * Clean up the timer and messaging window for this thread.
1.174 + */
1.175 +
1.176 + if (tsdPtr->hwnd) {
1.177 + KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
1.178 + DestroyWindow(tsdPtr->hwnd);
1.179 + }
1.180 +
1.181 + /*
1.182 + * If this is the last thread to use the notifier, unregister
1.183 + * the notifier window class.
1.184 + */
1.185 +
1.186 + Tcl_MutexLock(¬ifierMutex);
1.187 + notifierCount--;
1.188 + if (notifierCount == 0) {
1.189 + UnregisterClassA("TclNotifier", TclWinGetTclInstance());
1.190 + }
1.191 + Tcl_MutexUnlock(¬ifierMutex);
1.192 +}
1.193 +
1.194 +/*
1.195 + *----------------------------------------------------------------------
1.196 + *
1.197 + * Tcl_AlertNotifier --
1.198 + *
1.199 + * Wake up the specified notifier from any thread. This routine
1.200 + * is called by the platform independent notifier code whenever
1.201 + * the Tcl_ThreadAlert routine is called. This routine is
1.202 + * guaranteed not to be called on a given notifier after
1.203 + * Tcl_FinalizeNotifier is called for that notifier. This routine
1.204 + * is typically called from a thread other than the notifier's
1.205 + * thread.
1.206 + *
1.207 + * Results:
1.208 + * None.
1.209 + *
1.210 + * Side effects:
1.211 + * Sends a message to the messaging window for the notifier
1.212 + * if there isn't already one pending.
1.213 + *
1.214 + *----------------------------------------------------------------------
1.215 + */
1.216 +
1.217 +void
1.218 +Tcl_AlertNotifier(clientData)
1.219 + ClientData clientData; /* Pointer to thread data. */
1.220 +{
1.221 + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
1.222 +
1.223 + /*
1.224 + * Note that we do not need to lock around access to the hwnd
1.225 + * because the race condition has no effect since any race condition
1.226 + * implies that the notifier thread is already awake.
1.227 + */
1.228 +
1.229 + if (tsdPtr->hwnd) {
1.230 + /*
1.231 + * We do need to lock around access to the pending flag.
1.232 + */
1.233 +
1.234 + EnterCriticalSection(&tsdPtr->crit);
1.235 + if (!tsdPtr->pending) {
1.236 + PostMessage(tsdPtr->hwnd, WM_WAKEUP, 0, 0);
1.237 + }
1.238 + tsdPtr->pending = 1;
1.239 + LeaveCriticalSection(&tsdPtr->crit);
1.240 + } else {
1.241 + SetEvent(tsdPtr->event);
1.242 + }
1.243 +}
1.244 +
1.245 +/*
1.246 + *----------------------------------------------------------------------
1.247 + *
1.248 + * Tcl_SetTimer --
1.249 + *
1.250 + * This procedure sets the current notifier timer value. The
1.251 + * notifier will ensure that Tcl_ServiceAll() is called after
1.252 + * the specified interval, even if no events have occurred.
1.253 + *
1.254 + * Results:
1.255 + * None.
1.256 + *
1.257 + * Side effects:
1.258 + * Replaces any previous timer.
1.259 + *
1.260 + *----------------------------------------------------------------------
1.261 + */
1.262 +
1.263 +void
1.264 +Tcl_SetTimer(
1.265 + Tcl_Time *timePtr) /* Maximum block time, or NULL. */
1.266 +{
1.267 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.268 + UINT timeout;
1.269 +
1.270 + /*
1.271 + * Allow the notifier to be hooked. This may not make sense
1.272 + * on Windows, but mirrors the UNIX hook.
1.273 + */
1.274 +
1.275 + if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
1.276 + tclStubs.tcl_SetTimer(timePtr);
1.277 + return;
1.278 + }
1.279 +
1.280 + /*
1.281 + * We only need to set up an interval timer if we're being called
1.282 + * from an external event loop. If we don't have a window handle
1.283 + * then we just return immediately and let Tcl_WaitForEvent handle
1.284 + * timeouts.
1.285 + */
1.286 +
1.287 + if (!tsdPtr->hwnd) {
1.288 + return;
1.289 + }
1.290 +
1.291 + if (!timePtr) {
1.292 + timeout = 0;
1.293 + } else {
1.294 + /*
1.295 + * Make sure we pass a non-zero value into the timeout argument.
1.296 + * Windows seems to get confused by zero length timers.
1.297 + */
1.298 +
1.299 + timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
1.300 + if (timeout == 0) {
1.301 + timeout = 1;
1.302 + }
1.303 + }
1.304 + tsdPtr->timeout = timeout;
1.305 + if (timeout != 0) {
1.306 + tsdPtr->timerActive = 1;
1.307 + SetTimer(tsdPtr->hwnd, INTERVAL_TIMER,
1.308 + (unsigned long) tsdPtr->timeout, NULL);
1.309 + } else {
1.310 + tsdPtr->timerActive = 0;
1.311 + KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
1.312 + }
1.313 +}
1.314 +
1.315 +/*
1.316 + *----------------------------------------------------------------------
1.317 + *
1.318 + * Tcl_ServiceModeHook --
1.319 + *
1.320 + * This function is invoked whenever the service mode changes.
1.321 + *
1.322 + * Results:
1.323 + * None.
1.324 + *
1.325 + * Side effects:
1.326 + * If this is the first time the notifier is set into
1.327 + * TCL_SERVICE_ALL, then the communication window is created.
1.328 + *
1.329 + *----------------------------------------------------------------------
1.330 + */
1.331 +
1.332 +void
1.333 +Tcl_ServiceModeHook(mode)
1.334 + int mode; /* Either TCL_SERVICE_ALL, or
1.335 + * TCL_SERVICE_NONE. */
1.336 +{
1.337 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.338 +
1.339 + /*
1.340 + * If this is the first time that the notifier has been used from a
1.341 + * modal loop, then create a communication window. Note that after
1.342 + * this point, the application needs to service events in a timely
1.343 + * fashion or Windows will hang waiting for the window to respond
1.344 + * to synchronous system messages. At some point, we may want to
1.345 + * consider destroying the window if we leave the modal loop, but
1.346 + * for now we'll leave it around.
1.347 + */
1.348 +
1.349 + if (mode == TCL_SERVICE_ALL && !tsdPtr->hwnd) {
1.350 + tsdPtr->hwnd = CreateWindowA("TclNotifier", "TclNotifier", WS_TILED,
1.351 + 0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL);
1.352 + /*
1.353 + * Send an initial message to the window to ensure that we wake up the
1.354 + * notifier once we get into the modal loop. This will force the
1.355 + * notifier to recompute the timeout value and schedule a timer
1.356 + * if one is needed.
1.357 + */
1.358 +
1.359 + Tcl_AlertNotifier((ClientData)tsdPtr);
1.360 + }
1.361 +}
1.362 +
1.363 +/*
1.364 + *----------------------------------------------------------------------
1.365 + *
1.366 + * NotifierProc --
1.367 + *
1.368 + * This procedure is invoked by Windows to process events on
1.369 + * the notifier window. Messages will be sent to this window
1.370 + * in response to external timer events or calls to
1.371 + * TclpAlertTsdPtr->
1.372 + *
1.373 + * Results:
1.374 + * A standard windows result.
1.375 + *
1.376 + * Side effects:
1.377 + * Services any pending events.
1.378 + *
1.379 + *----------------------------------------------------------------------
1.380 + */
1.381 +
1.382 +static LRESULT CALLBACK
1.383 +NotifierProc(
1.384 + HWND hwnd,
1.385 + UINT message,
1.386 + WPARAM wParam,
1.387 + LPARAM lParam)
1.388 +{
1.389 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.390 +
1.391 + if (message == WM_WAKEUP) {
1.392 + EnterCriticalSection(&tsdPtr->crit);
1.393 + tsdPtr->pending = 0;
1.394 + LeaveCriticalSection(&tsdPtr->crit);
1.395 + } else if (message != WM_TIMER) {
1.396 + return DefWindowProc(hwnd, message, wParam, lParam);
1.397 + }
1.398 +
1.399 + /*
1.400 + * Process all of the runnable events.
1.401 + */
1.402 +
1.403 + Tcl_ServiceAll();
1.404 + return 0;
1.405 +}
1.406 +
1.407 +/*
1.408 + *----------------------------------------------------------------------
1.409 + *
1.410 + * Tcl_WaitForEvent --
1.411 + *
1.412 + * This function is called by Tcl_DoOneEvent to wait for new
1.413 + * events on the message queue. If the block time is 0, then
1.414 + * Tcl_WaitForEvent just polls the event queue without blocking.
1.415 + *
1.416 + * Results:
1.417 + * Returns -1 if a WM_QUIT message is detected, returns 1 if
1.418 + * a message was dispatched, otherwise returns 0.
1.419 + *
1.420 + * Side effects:
1.421 + * Dispatches a message to a window procedure, which could do
1.422 + * anything.
1.423 + *
1.424 + *----------------------------------------------------------------------
1.425 + */
1.426 +
1.427 +int
1.428 +Tcl_WaitForEvent(
1.429 + Tcl_Time *timePtr) /* Maximum block time, or NULL. */
1.430 +{
1.431 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.432 + MSG msg;
1.433 + DWORD timeout, result;
1.434 + int status;
1.435 +
1.436 + /*
1.437 + * Allow the notifier to be hooked. This may not make
1.438 + * sense on windows, but mirrors the UNIX hook.
1.439 + */
1.440 +
1.441 + if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
1.442 + return tclStubs.tcl_WaitForEvent(timePtr);
1.443 + }
1.444 +
1.445 + /*
1.446 + * Compute the timeout in milliseconds.
1.447 + */
1.448 +
1.449 + if (timePtr) {
1.450 + timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
1.451 + } else {
1.452 + timeout = INFINITE;
1.453 + }
1.454 +
1.455 + /*
1.456 + * Check to see if there are any messages in the queue before waiting
1.457 + * because MsgWaitForMultipleObjects will not wake up if there are events
1.458 + * currently sitting in the queue.
1.459 + */
1.460 +
1.461 + if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
1.462 + /*
1.463 + * Wait for something to happen (a signal from another thread, a
1.464 + * message, or timeout).
1.465 + */
1.466 +
1.467 + result = MsgWaitForMultipleObjects(1, &tsdPtr->event, FALSE, timeout,
1.468 + QS_ALLINPUT);
1.469 + }
1.470 +
1.471 + /*
1.472 + * Check to see if there are any messages to process.
1.473 + */
1.474 +
1.475 + if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
1.476 + /*
1.477 + * Retrieve and dispatch the first message.
1.478 + */
1.479 +
1.480 + result = GetMessage(&msg, NULL, 0, 0);
1.481 + if (result == 0) {
1.482 + /*
1.483 + * We received a request to exit this thread (WM_QUIT), so
1.484 + * propagate the quit message and start unwinding.
1.485 + */
1.486 +
1.487 + PostQuitMessage((int) msg.wParam);
1.488 + status = -1;
1.489 + } else if (result == -1) {
1.490 + /*
1.491 + * We got an error from the system. I have no idea why this would
1.492 + * happen, so we'll just unwind.
1.493 + */
1.494 +
1.495 + status = -1;
1.496 + } else {
1.497 + TranslateMessage(&msg);
1.498 + DispatchMessage(&msg);
1.499 + status = 1;
1.500 + }
1.501 + } else {
1.502 + status = 0;
1.503 + }
1.504 +
1.505 + ResetEvent(tsdPtr->event);
1.506 + return status;
1.507 +}
1.508 +
1.509 +/*
1.510 + *----------------------------------------------------------------------
1.511 + *
1.512 + * Tcl_Sleep --
1.513 + *
1.514 + * Delay execution for the specified number of milliseconds.
1.515 + *
1.516 + * Results:
1.517 + * None.
1.518 + *
1.519 + * Side effects:
1.520 + * Time passes.
1.521 + *
1.522 + *----------------------------------------------------------------------
1.523 + */
1.524 +
1.525 +void
1.526 +Tcl_Sleep(ms)
1.527 + int ms; /* Number of milliseconds to sleep. */
1.528 +{
1.529 + /*
1.530 + * Simply calling 'Sleep' for the requisite number of milliseconds
1.531 + * can make the process appear to wake up early because it isn't
1.532 + * synchronized with the CPU performance counter that is used in
1.533 + * tclWinTime.c. This behavior is probably benign, but messes
1.534 + * up some of the corner cases in the test suite. We get around
1.535 + * this problem by repeating the 'Sleep' call as many times
1.536 + * as necessary to make the clock advance by the requisite amount.
1.537 + */
1.538 +
1.539 + Tcl_Time now; /* Current wall clock time */
1.540 + Tcl_Time desired; /* Desired wakeup time */
1.541 + DWORD sleepTime = ms; /* Time to sleep */
1.542 +
1.543 + Tcl_GetTime( &now );
1.544 + desired.sec = now.sec + ( ms / 1000 );
1.545 + desired.usec = now.usec + 1000 * ( ms % 1000 );
1.546 + if ( desired.usec > 1000000 ) {
1.547 + ++desired.sec;
1.548 + desired.usec -= 1000000;
1.549 + }
1.550 +
1.551 + for ( ; ; ) {
1.552 + Sleep( sleepTime );
1.553 + Tcl_GetTime( &now );
1.554 + if ( now.sec > desired.sec ) {
1.555 + break;
1.556 + } else if ( ( now.sec == desired.sec )
1.557 + && ( now.usec >= desired.usec ) ) {
1.558 + break;
1.559 + }
1.560 + sleepTime = ( ( 1000 * ( desired.sec - now.sec ) )
1.561 + + ( ( desired.usec - now.usec ) / 1000 ) );
1.562 + }
1.563 +
1.564 +}