I'm doing a small timer and decided to use a thread for it... I have just finished the program, it works fine, but not in the right time, i tried using Sleep, (some number) but it somehow stop my program from working... I'm working with minutes, seconds and second/100 (since I don't know how it's called I'm calling them milisecond, but they're not miliseconds, they are second/100).

Here's my thread:
ThreadProc proc DlgHandle:DWORD
@@:
    cmp KillThread, TRUE
    jz quit

    invoke GetDlgItemInt, DlgHandle, IDC_MILISECONDS, NULL, FALSE
    .if eax > 998
        invoke GetDlgItemInt, DlgHandle, IDC_SECONDS, NULL, FALSE
        .if eax > 58
            invoke GetDlgItemInt, DlgHandle, IDC_MINUTES, NULL, FALSE
            .if eax > 58
                invoke MessageBox, DlgHandle, 0, 0, MB_OK
                jmp quit
            .else
                inc eax
                invoke SetDlgItemInt, DlgHandle, IDC_MINUTES, eax, FALSE
            .endif
            invoke SetDlgItemInt, DlgHandle, IDC_SECONDS, 0, FALSE
        .else
        inc eax
        invoke SetDlgItemInt, DlgHandle, IDC_SECONDS, eax, FALSE
        .endif
        invoke SetDlgItemInt, DlgHandle, IDC_MILISECONDS, 0, FALSE
    .else
        ; wait a second/100 (invoke Sleep, 100 ?)
    inc eax
    invoke SetDlgItemInt, DlgHandle, IDC_MILISECONDS, eax, FALSE
    .endif
    jmp @B
quit:
    mov KillThread, FALSE
    ret
ThreadProc endp


When I put sleep, 100 in the code it simply freezes at 1.
Posted on 2005-09-28 21:19:32 by Lenin
Humm... remember that any API call can (read: will) trash EAX,ECX,EDX... perhaps that's your problem on calling Sleep() ?
Posted on 2005-09-29 05:22:35 by f0dder
Oh thanks :) I will just leave the code here in case anyone wants to make it more accurate:

ThreadProc proc DlgHandle:DWORD
@@:
    cmp KillThread, TRUE
    jz quit

    invoke GetDlgItemInt, DlgHandle, IDC_MILISECONDS, NULL, FALSE
    .if eax > 98
        invoke GetDlgItemInt, DlgHandle, IDC_SECONDS, NULL, FALSE
        .if eax > 58
            invoke GetDlgItemInt, DlgHandle, IDC_MINUTES, NULL, FALSE
            .if eax > 58
                jmp quit
            .else
                inc eax
                invoke SetDlgItemInt, DlgHandle, IDC_MINUTES, eax, FALSE
            .endif
            invoke SetDlgItemInt, DlgHandle, IDC_SECONDS, 0, FALSE
        .else
        inc eax
        invoke SetDlgItemInt, DlgHandle, IDC_SECONDS, eax, FALSE
        .endif
        invoke SetDlgItemInt, DlgHandle, IDC_MILISECONDS, 0, FALSE
    .else
    inc eax
    invoke SetDlgItemInt, DlgHandle, IDC_MILISECONDS, eax, FALSE
    invoke Sleep, 1
    .endif
    jmp @B
quit:
    mov KillThread, FALSE
    ret
ThreadProc endp
Posted on 2005-09-29 17:01:00 by Lenin
By the way, what's the purpose? To count down, and show a graphical countdown timer while doing so?
Posted on 2005-09-29 17:30:37 by f0dder
No... Just a timer...
Attachments:
Posted on 2005-09-29 18:13:25 by Lenin
second/10 = Deciseconds (like Decade/Decimal)
second/100 = Centiseconds (like Century)
second/1000 = Milliseconds (like Millenium)
second/1000000 = Microseconds
second/1000000000 = Nanoseconds

etc...
Posted on 2005-09-29 18:36:54 by SpooK
Hmm, your code is more complex than it needs to be :)

It would be easier to update a single "ticks" value, then do a series of divisions get mili, seconds, hours from that value - instead of bumping up editboxes and having nested IF sentences.

Also note that "Sleep" doesn't have very good precision - you're limited to ~12ms or so iirc. There's a couple of ways around this, but unless you need very precise timing, the best would be some delay+time function.
Posted on 2005-09-29 18:37:47 by f0dder
@SpooK
    Just updated my code with centiseconds :)

@f0dder
    I got intesrested in your idea, would mind explaining it a little more? :)
Posted on 2005-09-29 19:06:02 by Lenin
Here's a sample of using divides, a little "uptime" program for windows. The approach is straightforward, start by dividing with the largest time-factor you want to support (in this case, hours), shuffle and divide the remainder by the next factor.
Attachments:
Posted on 2005-09-29 19:55:23 by f0dder
I understood your aproach, but ain't that a clock? How would I turn that into a timer?
Posted on 2005-09-29 20:11:10 by Lenin
Well, that was just the "display timer/clock" code.

Now, as for updating your loop, let's start with a simple approach :)


@@loop:
    invoke  Sleep, TIMEAMOUNT
    add    , TIMEAMOUNT
    invoke  formatTick    ; use the DIVide-and-conquer method, update controls
    jmp    @@loop


Since the granularity of Sleep() is miliseconds, TIMEAMOUNT is specified in msec as well. Just realize that Sleep() doesn't really give you this fine granularity, so I wouldn't use this approach for TIMEAMOUNT values less than 25msec - this should be fine for a lot of purposes.

For other purposes, you can either sit tight and burn CPU cycles in a polling loop (not a good solution in most cases), or you can decouple timing and the user int
Posted on 2005-09-29 20:36:06 by f0dder
Why not use SetTimer() or CreateWaitableTimer()?

Using Sleep() for this task is silly (in fact, justifyable use of Sleep() is rare). It may return years after the interval given to it has passed. There's no real granularity for Sleep(), perhaps unless you've a real-time priority thread in a real-time priority-class process.
Posted on 2005-09-29 22:16:30 by death
death, I intended to cover that in upcoming posts - change a little at a time, keep things easy to follow :)
Posted on 2005-09-30 00:40:33 by f0dder
Hi Everybody.

death is right, this is better way:


          invoke  GetTickCount
          mov dwStartTime, eax

...

@@:    invoke  GetTickCount
          sub eax, dwStartTime
          invoke  FormatTick, eax
          invoke  Sleep, 100
          jmp  @B


or use QueryPerformanceCounter / QueryPerformanceFrequency if You need better precision

------------------
Best regards
Posted on 2005-09-30 04:52:06 by Bohdan
I confess I don't know what the noise is about with the SleepEx() API. It uses NTDLL.DLL to make an int 2Eh call, it is a published API that has been around for years and it is well suited for a low resolution timer. If it is put in a seperate thread you can send messages to the timer to change its duration on the fly, change its priority and the like and it has the advantage that even if you set it to a high priority, the SleepEx() API yields unconditionally when it is set to 1 or greater ms so it will not lock the machine up.

Any normal timer is subject to timeslice interval and if a machine is very heavily loaded with higher priority tasks, they will start to become unreliable. If you actually need higher resolution timers, you would normaly use a multimedia timer. The timeslice interval varies from OS version to OS version but if you thing in terms of about 20 ms you get some idea of what you can set as the bottom limit in duration with SleepEx().
Posted on 2005-09-30 18:13:10 by hutch--

I confess I don't know what the noise is about with the SleepEx() API.

Then try reading his code.

It original depends 100% on the accuracy of Sleep - we all know that it isn't very accurate, and that Sleep(1) certeinly WON'T return in 1ms because of the granularity. Basing a timer on that will give you pretty funky results, adding up over time.

For what Lenin is doing, the best solution is decoupling the timer code and the UI updating code - getting the timer (absolute value or delta) from a proper synchronized timer. Even GetTickCount is better than depending on Sleep() and incrementing a variable.

Using Sleep in the UI updating thread is fine though, since you don't need to update the UI controls faster than the human eye can register. I would have opted for WM_TIMER style notification instead though, to avoid the overhead of an additional thread.
Posted on 2005-09-30 18:55:36 by f0dder
Uh-oh, another flaming thread is about to be born... ;)
Posted on 2005-09-30 19:36:09 by QvasiModo
QvasiModo, no thanks - hutch can take it elsewhere if he wants to :)

Basing a timer on Sleep just isn't a very good solution, especially when there's easier & better methods around without disadvantages.
Posted on 2005-09-30 19:52:41 by f0dder
I changed my code, now using SetTimer... It seems to be more accurate now.
Attachments:
Posted on 2005-09-30 21:21:57 by Lenin
Just beware that SetTimer, the way you use it, still isn't extremely accurate.
Posted on 2005-09-30 21:39:20 by f0dder