Tcl Source Code

Check-in [8ea9c4081c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:[1e2c6ce4] Monotonic clock for MS-Windows. Extracted from branch [tkt3328635-posix-monotonic-clock] and corresponding ticket [3328635]. Thanks, Christian!
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | 1e2c6ce4-mswin-monotonic-clock
Files: files | file ages | folders
SHA3-256: 8ea9c4081c39b8bf9e2be7da23be2f039dc9c1aca6ea3be89b1b6a757dc9d4d8
User & Date: oehhar 2025-05-13 19:58:46.740
Context
2025-05-21
13:02
[1e2c6ce4], TIP 723: document interp limit to be dependent on monotonic clock on Windows platform Leaf check-in: 8c56c95f93 user: oehhar tags: 1e2c6ce4-mswin-monotonic-clock
2025-05-13
19:58
[1e2c6ce4] Monotonic clock for MS-Windows. Extracted from branch [tkt3328635-posix-monotonic-clock] ... check-in: 8ea9c4081c user: oehhar tags: 1e2c6ce4-mswin-monotonic-clock
2025-05-12
11:33
Remove Tcl_GetString() stub entry: it isn't used anywhere any more check-in: fdcf6c024c user: jan.nijtmans tags: trunk, main
Changes
Unified Diff Ignore Whitespace Patch
Changes to generic/tclInt.h.
3654
3655
3656
3657
3658
3659
3660









3661
3662
3663
3664
3665
3666
3667
 * optimization (fragile on changes) in one place.
 */

MODULE_SCOPE int	TclIsSpaceProc(int byte);
#define TclIsSpaceProcM(byte) \
    (((byte) > 0x20) ? 0 : TclIsSpaceProc(byte))










/*
 *----------------------------------------------------------------
 * Command procedures in the generic core:
 *----------------------------------------------------------------
 */

MODULE_SCOPE Tcl_ObjCmdProc Tcl_AfterObjCmd;







>
>
>
>
>
>
>
>
>







3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
 * optimization (fragile on changes) in one place.
 */

MODULE_SCOPE int	TclIsSpaceProc(int byte);
#define TclIsSpaceProcM(byte) \
    (((byte) > 0x20) ? 0 : TclIsSpaceProc(byte))

/*
 * Monotonic time independent on the wall clock
 * MS-WIndows part extracted from ticket [3328635fff]
 */

#ifdef WIN32
MODULE_SCOPE int	TclpGetMonotonicTime(Tcl_Time *timePtr);
#endif

/*
 *----------------------------------------------------------------
 * Command procedures in the generic core:
 *----------------------------------------------------------------
 */

MODULE_SCOPE Tcl_ObjCmdProc Tcl_AfterObjCmd;
Changes to generic/tclInterp.c.
3440
3441
3442
3443
3444
3445
3446








3447

3448
3449
3450
3451
3452
3453
3454
    }

    if ((iPtr->limit.active & TCL_LIMIT_TIME) &&
	    ((iPtr->limit.timeGranularity == 1) ||
		(ticker % iPtr->limit.timeGranularity == 0))) {
	Tcl_Time now;









	Tcl_GetTime(&now);

	if (iPtr->limit.time.sec < now.sec ||
		(iPtr->limit.time.sec == now.sec &&
		iPtr->limit.time.usec < now.usec)) {
	    iPtr->limit.exceeded |= TCL_LIMIT_TIME;
	    Tcl_Preserve(interp);
	    RunLimitHandlers(iPtr->limit.timeHandlers, interp);
	    if (iPtr->limit.time.sec > now.sec ||







>
>
>
>
>
>
>
>

>







3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
    }

    if ((iPtr->limit.active & TCL_LIMIT_TIME) &&
	    ((iPtr->limit.timeGranularity == 1) ||
		(ticker % iPtr->limit.timeGranularity == 0))) {
	Tcl_Time now;

	/*
	 * Monotonic time independent on the wall clock
	 * MS-WIndows part extracted from ticket [3328635fff]
	 */

#ifdef WIN32
	TclpGetMonotonicTime(&now);
#else
	Tcl_GetTime(&now);
#endif
	if (iPtr->limit.time.sec < now.sec ||
		(iPtr->limit.time.sec == now.sec &&
		iPtr->limit.time.usec < now.usec)) {
	    iPtr->limit.exceeded |= TCL_LIMIT_TIME;
	    Tcl_Preserve(interp);
	    RunLimitHandlers(iPtr->limit.timeHandlers, interp);
	    if (iPtr->limit.time.sec > now.sec ||
3993
3994
3995
3996
3997
3998
3999


























4000
4001
4002
4003
4004
4005
4006
void
Tcl_LimitSetTime(
    Tcl_Interp *interp,
    Tcl_Time *timeLimitPtr)
{
    Interp *iPtr = (Interp *) interp;
    Tcl_Time nextMoment;



























    memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time));
    if (iPtr->limit.timeEvent != NULL) {
	Tcl_DeleteTimerHandler(iPtr->limit.timeEvent);
    }
    nextMoment.sec = timeLimitPtr->sec;
    nextMoment.usec = timeLimitPtr->usec+10;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
void
Tcl_LimitSetTime(
    Tcl_Interp *interp,
    Tcl_Time *timeLimitPtr)
{
    Interp *iPtr = (Interp *) interp;
    Tcl_Time nextMoment;

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
    Tcl_Time mono, real, limit;
    if (TclpGetMonotonicTime(&mono)) {
	Tcl_GetTime(&real);
	limit = *timeLimitPtr;
	limit.sec -= real.sec;
	limit.usec -= real.usec;
	if (limit.usec < 0) {
	    limit.sec -= 1;
	    limit.usec += 1000000;
	}
	limit.sec += mono.sec;
	limit.usec += mono.usec;
	if (limit.usec >= 1000000) {
	    limit.sec += 1;
	    limit.usec -= 1000000;
	}
	timeLimitPtr = &limit;
    }
#endif

    memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time));
    if (iPtr->limit.timeEvent != NULL) {
	Tcl_DeleteTimerHandler(iPtr->limit.timeEvent);
    }
    nextMoment.sec = timeLimitPtr->sec;
    nextMoment.usec = timeLimitPtr->usec+10;
4078
4079
4080
4081
4082
4083
4084



























4085
4086
4087
4088
4089
4090
4091
void
Tcl_LimitGetTime(
    Tcl_Interp *interp,
    Tcl_Time *timeLimitPtr)
{
    Interp *iPtr = (Interp *) interp;




























    memcpy(timeLimitPtr, &iPtr->limit.time, sizeof(Tcl_Time));
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_LimitSetGranularity --







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
void
Tcl_LimitGetTime(
    Tcl_Interp *interp,
    Tcl_Time *timeLimitPtr)
{
    Interp *iPtr = (Interp *) interp;

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
    Tcl_Time mono, real, limit;

    if (TclpGetMonotonicTime(&mono)) {
	Tcl_GetTime(&real);
	limit = iPtr->limit.time;
	limit.sec -= mono.sec;
	limit.usec -= mono.usec;
	if (limit.usec < 0) {
	    limit.sec -= 1;
	    limit.usec += 1000000;
	}
	limit.sec += real.sec;
	limit.usec += real.usec;
	if (limit.usec >= 1000000) {
	    limit.sec += 1;
	    limit.usec -= 1000000;
	}
	memcpy(timeLimitPtr, &limit, sizeof(Tcl_Time));
	return;
    }
#endif
    memcpy(timeLimitPtr, &iPtr->limit.time, sizeof(Tcl_Time));
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_LimitSetGranularity --
Changes to generic/tclTimer.c.
255
256
257
258
259
260
261








262

263
264
265
266
267
268
269
{
    Tcl_Time time;

    /*
     * Compute when the event should fire.
     */









    Tcl_GetTime(&time);

    time.sec += milliseconds/1000;
    time.usec += (milliseconds%1000)*1000;
    if (time.usec >= 1000000) {
	time.usec -= 1000000;
	time.sec += 1;
    }
    return TclCreateAbsoluteTimerHandler(&time, proc, clientData);







>
>
>
>
>
>
>
>

>







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
{
    Tcl_Time time;

    /*
     * Compute when the event should fire.
     */

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
    TclpGetMonotonicTime(&time);
#else
    Tcl_GetTime(&time);
#endif
    time.sec += milliseconds/1000;
    time.usec += (milliseconds%1000)*1000;
    if (time.usec >= 1000000) {
	time.usec -= 1000000;
	time.sec += 1;
    }
    return TclCreateAbsoluteTimerHandler(&time, proc, clientData);
413
414
415
416
417
418
419








420

421
422
423
424
425
426
427
	blockTime.sec = 0;
	blockTime.usec = 0;
    } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
	/*
	 * Compute the timeout for the next timer on the list.
	 */









	Tcl_GetTime(&blockTime);

	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
	blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
		blockTime.usec;
	if (blockTime.usec < 0) {
	    blockTime.sec -= 1;
	    blockTime.usec += 1000000;
	}







>
>
>
>
>
>
>
>

>







422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
	blockTime.sec = 0;
	blockTime.usec = 0;
    } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
	/*
	 * Compute the timeout for the next timer on the list.
	 */

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
	TclpGetMonotonicTime(&blockTime);
#else
	Tcl_GetTime(&blockTime);
#endif
	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
	blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
		blockTime.usec;
	if (blockTime.usec < 0) {
	    blockTime.sec -= 1;
	    blockTime.usec += 1000000;
	}
464
465
466
467
468
469
470








471

472
473
474
475
476
477
478
    ThreadSpecificData *tsdPtr = InitTimer();

    if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
	/*
	 * Compute the timeout for the next timer on the list.
	 */









	Tcl_GetTime(&blockTime);

	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
	blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
		blockTime.usec;
	if (blockTime.usec < 0) {
	    blockTime.sec -= 1;
	    blockTime.usec += 1000000;
	}







>
>
>
>
>
>
>
>

>







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
    ThreadSpecificData *tsdPtr = InitTimer();

    if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
	/*
	 * Compute the timeout for the next timer on the list.
	 */

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
	TclpGetMonotonicTime(&blockTime);
#else
	Tcl_GetTime(&blockTime);
#endif
	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
	blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
		blockTime.usec;
	if (blockTime.usec < 0) {
	    blockTime.sec -= 1;
	    blockTime.usec += 1000000;
	}
560
561
562
563
564
565
566









567

568
569
570
571
572
573
574
     *	  most recently created handler appearing after earlier ones with the
     *	  same expiration time, we don't have to worry about newer generation
     *	  timers appearing before later ones.
     */

    tsdPtr->timerPending = 0;
    currentTimerId = tsdPtr->lastTimerId;









    Tcl_GetTime(&time);

    while (1) {
	nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
	timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
	if (timerHandlerPtr == NULL) {
	    break;
	}








>
>
>
>
>
>
>
>
>

>







587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
     *	  most recently created handler appearing after earlier ones with the
     *	  same expiration time, we don't have to worry about newer generation
     *	  timers appearing before later ones.
     */

    tsdPtr->timerPending = 0;
    currentTimerId = tsdPtr->lastTimerId;

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
    TclpGetMonotonicTime(&time);
#else
    Tcl_GetTime(&time);
#endif
    while (1) {
	nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
	timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
	if (timerHandlerPtr == NULL) {
	    break;
	}

861
862
863
864
865
866
867









868

869
870
871
872
873
874
875
	 * the future, and wrap-around is unlikely to occur in less than about
	 * 1-10 years. Thus it's unlikely that any old ids will still be
	 * around when wrap-around occurs.
	 */

	afterPtr->id = tsdPtr->afterId;
	tsdPtr->afterId += 1;









	Tcl_GetTime(&wakeup);

	wakeup.sec += ms / 1000;
	wakeup.usec += ms % 1000 * 1000;
	if (wakeup.usec > 1000000) {
	    wakeup.sec++;
	    wakeup.usec -= 1000000;
	}
	afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup,







>
>
>
>
>
>
>
>
>

>







898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
	 * the future, and wrap-around is unlikely to occur in less than about
	 * 1-10 years. Thus it's unlikely that any old ids will still be
	 * around when wrap-around occurs.
	 */

	afterPtr->id = tsdPtr->afterId;
	tsdPtr->afterId += 1;

    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
	TclpGetMonotonicTime(&wakeup);
#else
	Tcl_GetTime(&wakeup);
#endif
	wakeup.sec += ms / 1000;
	wakeup.usec += ms % 1000 * 1000;
	if (wakeup.usec > 1000000) {
	    wakeup.sec++;
	    wakeup.usec -= 1000000;
	}
	afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup,
1008
1009
1010
1011
1012
1013
1014









1015

1016
1017
1018
1019
1020
1021
1022
    Tcl_WideInt ms)
{
    Interp *iPtr = (Interp *) interp;

    Tcl_Time endTime, now;
    Tcl_WideInt diff;










    Tcl_GetTime(&now);

    endTime = now;
    endTime.sec += (ms / 1000);
    endTime.usec += (ms % 1000) * 1000;
    if (endTime.usec >= 1000000) {
	endTime.sec++;
	endTime.usec -= 1000000;
    }







>
>
>
>
>
>
>
>
>

>







1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
    Tcl_WideInt ms)
{
    Interp *iPtr = (Interp *) interp;

    Tcl_Time endTime, now;
    Tcl_WideInt diff;


    /*
     * Monotonic time independent on the wall clock
     * MS-WIndows part extracted from ticket [3328635fff]
     */

#ifdef WIN32
    TclpGetMonotonicTime(&now);
#else
    Tcl_GetTime(&now);
#endif
    endTime = now;
    endTime.sec += (ms / 1000);
    endTime.usec += (ms % 1000) * 1000;
    if (endTime.usec >= 1000000) {
	endTime.sec++;
	endTime.usec -= 1000000;
    }
1070
1071
1072
1073
1074
1075
1076




1077

1078
1079
1080
1081
1082
1083
1084
	    if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) {
		return TCL_ERROR;
	    }
	    if (Tcl_LimitCheck(interp) != TCL_OK) {
		return TCL_ERROR;
	    }
	}




	Tcl_GetTime(&now);

    } while (TCL_TIME_BEFORE(now, endTime));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *







>
>
>
>

>







1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
	    if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) {
		return TCL_ERROR;
	    }
	    if (Tcl_LimitCheck(interp) != TCL_OK) {
		return TCL_ERROR;
	    }
	}

#ifdef WIN32
	TclpGetMonotonicTime(&now);
#else
	Tcl_GetTime(&now);
#endif
    } while (TCL_TIME_BEFORE(now, endTime));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
Changes to win/tclWinInit.c.
690
691
692
693
694
695
696



























697
698
699
700
701
702
703
    *lengthPtr = i;

  done:
    Tcl_DStringFree(&envString);
    Tcl_Free(nameUpper);
    return result;
}




























/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
    *lengthPtr = i;

  done:
    Tcl_DStringFree(&envString);
    Tcl_Free(nameUpper);
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * TclpGetMononoticTime --
 *
 *	Like Tcl_GetTime() but return a monotonic clock source,
 *
 * Results:
 *	1 if monotonic.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TclpGetMonotonicTime(Tcl_Time *timePtr)
{
    ULONGLONG ms;

    ms = GetTickCount64();
    timePtr->sec = (long)(ms/1000);
    timePtr->usec = ((long)(ms%1000))*1000;
    return 1;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
Changes to win/tclWinNotify.c.
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
    int status;

    /*
     * Compute the timeout in milliseconds.
     */

    if (timePtr) {
	/*
	 * TIP #233 (Virtualized Time). Convert virtual domain delay to
	 * real-time.
	 */

	Tcl_Time myTime;

	myTime.sec  = timePtr->sec;
	myTime.usec = timePtr->usec;

	if (myTime.sec != 0 || myTime.usec != 0) {
	    TclScaleTime(&myTime);
	}

	timeout = (DWORD)myTime.sec * 1000 + (unsigned long)myTime.usec / 1000;
    } else {
	timeout = INFINITE;
    }

    /*
     * Check to see if there are any messages in the queue before waiting
     * because MsgWaitForMultipleObjects will not wake up if there are events







<
<
<
<
|
<
|
<
<
|
<
<
|
<
<







472
473
474
475
476
477
478




479

480


481


482


483
484
485
486
487
488
489
    int status;

    /*
     * Compute the timeout in milliseconds.
     */

    if (timePtr) {




	timeout = timePtr->sec * 1000 + timePtr->usec / 1000;

	if (timeout == INFINITE) {


	    timeout--;


	} 


    } else {
	timeout = INFINITE;
    }

    /*
     * Check to see if there are any messages in the queue before waiting
     * because MsgWaitForMultipleObjects will not wake up if there are events
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613



614
615
616
617
618
619
620
621
622
623
624
625
626
627
628



629
630
631
632
633
634
635
636
637
638
    Tcl_Time vdelay;		/* Time to sleep, for scaling virtual ->
				 * real. */
    DWORD sleepTime;		/* Time to sleep, real-time */

    vdelay.sec  = ms / 1000;
    vdelay.usec = (ms % 1000) * 1000;

    Tcl_GetTime(&now);
    desired.sec  = now.sec  + vdelay.sec;
    desired.usec = now.usec + vdelay.usec;
    if (desired.usec > 1000000) {
	++desired.sec;
	desired.usec -= 1000000;
    }

    /*
     * TIP #233: Scale delay from virtual to real-time.
     */

    TclScaleTime(&vdelay);
    sleepTime = (DWORD)vdelay.sec * 1000 + (unsigned long)vdelay.usec / 1000;




    for (;;) {
	SleepEx(sleepTime, TRUE);
	Tcl_GetTime(&now);
	if (now.sec > desired.sec) {
	    break;
	} else if ((now.sec == desired.sec) && (now.usec >= desired.usec)) {
	    break;
	}

	vdelay.sec  = desired.sec  - now.sec;
	vdelay.usec = desired.usec - now.usec;

	TclScaleTime(&vdelay);
	sleepTime = (DWORD)vdelay.sec * 1000 + (unsigned long)vdelay.usec / 1000;



    }
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */







|













>
>
>



|











>
>
>










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
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
    Tcl_Time vdelay;		/* Time to sleep, for scaling virtual ->
				 * real. */
    DWORD sleepTime;		/* Time to sleep, real-time */

    vdelay.sec  = ms / 1000;
    vdelay.usec = (ms % 1000) * 1000;

    TclpGetMonotonicTime(&now);
    desired.sec  = now.sec  + vdelay.sec;
    desired.usec = now.usec + vdelay.usec;
    if (desired.usec > 1000000) {
	++desired.sec;
	desired.usec -= 1000000;
    }

    /*
     * TIP #233: Scale delay from virtual to real-time.
     */

    TclScaleTime(&vdelay);
    sleepTime = (DWORD)vdelay.sec * 1000 + (unsigned long)vdelay.usec / 1000;
    if (sleepTime == INFINITE) {
	--sleepTime;
    }

    for (;;) {
	SleepEx(sleepTime, TRUE);
	TclpGetMonotonicTime(&now);
	if (now.sec > desired.sec) {
	    break;
	} else if ((now.sec == desired.sec) && (now.usec >= desired.usec)) {
	    break;
	}

	vdelay.sec  = desired.sec  - now.sec;
	vdelay.usec = desired.usec - now.usec;

	TclScaleTime(&vdelay);
	sleepTime = (DWORD)vdelay.sec * 1000 + (unsigned long)vdelay.usec / 1000;
	if (sleepTime == INFINITE) {
	    --sleepTime;
	}
    }
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
Changes to win/tclWinTime.c.
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211

void
Tcl_SetTimeProc(
    Tcl_GetTimeProc *getProc,
    Tcl_ScaleTimeProc *scaleProc,
    void *clientData)
{
    tclGetTimeProcPtr = getProc;
    tclScaleTimeProcPtr = scaleProc;
    tclTimeClientData = clientData;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_QueryTimeProc --
 *







<
<
<







1195
1196
1197
1198
1199
1200
1201



1202
1203
1204
1205
1206
1207
1208

void
Tcl_SetTimeProc(
    Tcl_GetTimeProc *getProc,
    Tcl_ScaleTimeProc *scaleProc,
    void *clientData)
{



}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_QueryTimeProc --
 *