CreateThread always ok, but CloseHandle fails sometimes

Started by cobold, May 22, 2009, 01:39:45 PM

Previous topic - Next topic

cobold

Hello,

please don't laugh about me when you see my code,  :lol but this is my first try to use threads.
In my code I create a thread called "Update". This thread displays the value of c64 (a qword var) every 5 seconds.
Problem is that the systems gives the thread an ID and Handle. Whenever the handle is #4, CloseHandle fails, else everything works fine.Looks like this:


C:\Asm>threadtest
Thread-ID(tID) = 1820 Thread Handle(th) = 4
        0t:00h:00m:05s 2099814064       00000000:7D289EB0
        0t:00h:00m:10s 4189059803       00000000:F9AFFADB
Ende    0t:00h:00m:10s 4294967296 hex = 00000001:00000000
Fehler CloseHandle 4
6 LastError: Invalid handle


C:\Asm>threadtest
Thread-ID(tID) = 284 Thread Handle(th) = 3
        0t:00h:00m:05s 2113449512       00000000:7DF8AE28
        0t:00h:00m:10s 4217235872       00000000:FB5DE9A0
Ende    0t:00h:00m:10s 4294967296 hex = 00000001:00000000



I think there must be an easy explanation/solution but me, being completely unexpierenced with threads not able to find it! I'm asking for help, because I wasted 2 hours on this and I'm stuck  :tdown


include \masm32\include\masm32rt.inc
; -------------------------------------------------------------------------
Update  PROTO :DWORD    ; thread displays c64 every 5000 ms
RunTime PROTO :DWORD    ; convert ms to "dd:hh:mm:ss"

.data?
    tID     dd ? ; Thread Update Identifier   
    th      dd ? ; Thread Handle
    msec    dd ?
.data
    TimeFmt db 9,"%ut:%02uh:%02um:%02us ",0
ALIGN 8   
    c64     dq 0 ; a 64-bit var as counter
; -------------------------------------------------------------------------
.code
start:
    call main
    ; ---------------------------------------------------
    ; whenever th == 4 -> ERROR_INVALID_HANDLE is returned
    ; if th != 4 everything is okay
    ; don't understand because CreateThread gave handle 4
    ; without error, but CloseHandle fails????!!!
    ; ---------------------------------------------------
    invoke CloseHandle,th
    .if eax==NULL
        push eax
        print "Fehler CloseHandle "
        print str$(th),13,10
        pop eax
        invoke GetLastError
        .if eax == ERROR_INVALID_HANDLE ;(6)
            print str$(eax)
            print " LastError: Invalid handle"
        .else
            print str$(eax)
            print " = LastError"
        .endif
    .endif
    print chr$(13,10,13,10)
    exit
; -------------------------------------------------------------------------
; Main
; -------------------------------------------------------------------------
main proc
    invoke GetTickCount
    mov msec,eax
    ; ---------------------------------------------------
    ; Create the thread - this works always w/o any error
    ; ---------------------------------------------------
    invoke CreateThread,0,0,OFFSET Update,0,0,OFFSET tID
    .if eax == NULL
        print "CreateThread Update failed!",13,10
    .else
        print "Thread-ID(tID) = "
        print str$(tID)
    .endif
    mov th,eax
    print " Thread Handle(th) = "
    print str$(th),13,10
   
_c64loop:
    add dword ptr c64[0],1
    jnc _cont
    adc dword ptr c64[4],0
_cont:
    cmp dword ptr[c64+4],1
    jne _c64loop

printit:
    print "Ende "       
    invoke GetTickCount
    sub eax,msec
    invoke RunTime,eax
    print uqword$(c64)," hex = "
    print uhex$(dword ptr[c64+4]),":"       ;lo
    print uhex$(dword ptr[c64+0]),13,10     ;hi
    ret
main endp

; -------------------------------------------------------------------------
; Thread
; -------------------------------------------------------------------------
align 4
Update proc Param1:DWORD

_updloop:
    invoke SleepEx,5000,TRUE
    invoke GetTickCount
    sub eax,msec
    invoke RunTime,eax
    print uqword$(c64),9
    print uhex$(dword ptr[c64+4]),":"       ;lo
    print uhex$(dword ptr[c64+0]),13,10     ;hi
    jmp _updloop
   
    ret
Update endp

align 4

RunTime proc ms:DWORD
    LOCAL ttt:DWORD
    LOCAL hhh:DWORD
    LOCAL mmm:DWORD
    LOCAL sss:DWORD
    LOCAL mss:DWORD

    xor edx,edx
    mov eax,ms
    mov ebx,86400000
    div ebx
    mov [ttt],eax
    mov eax,edx
    xor edx,edx
    mov ebx,3600000
    div ebx
    mov [hhh],eax
    mov eax,edx
    xor edx,edx
    mov ebx,60000
    div ebx
    mov [mmm],eax
    mov eax,edx
    xor edx,edx
    mov ebx,1000
    div ebx
    mov [sss],eax
    mov eax,edx
    xor edx,edx
    mov ebx,10
    div ebx
    mov mss,eax
    invoke crt_printf,ADDR TimeFmt,ttt,hhh,mmm,sss,mss
    ret
RunTime endp
end start

dedndave

INVOKE GetLastError immediately after the CloseHandle fails
push the error value (not the 0) to print error message

when you use "print", it invokes stdout i think - that destroys the LastError value

this may not correct the failure, but at least you'll get the right error code value

i might be inclined to use variable names that are slightly longer - lol
th - ThdHndl - or ThreadHandle

to help debug the problem, display the thread handle value right before attempting to close it
you are probably trying to close a handle that isn't open (i.e. the handle is not the right value)

    call main
    ; ---------------------------------------------------
    ; whenever th == 4 -> ERROR_INVALID_HANDLE is returned
    ; if th != 4 everything is okay
    ; don't understand because CreateThread gave handle 4
    ; without error, but CloseHandle fails????!!!
    ; ---------------------------------------------------
    print str$(th)
    invoke CloseHandle,th
    .if eax==NULL
        invoke GetLastError
        push eax
        print "Fehler CloseHandle "
        print str$(th),13,10
        pop eax

cobold

Hi dedndave,

thanks for the reply, problem still unsolved:

Quote from: dedndave on May 22, 2009, 02:20:54 PM
INVOKE GetLastError immediately after the CloseHandle fails
...
when you use "print", it invokes stdout i think - that destroys the LastError value
Thanks for that info, I changed that according to your suggestion:
    invoke CloseHandle,th     ; th is correct value - I checked that
    .if eax==NULL ; CloseHandle fails
        invoke GetLastError     ; call GetLastError IMMED after
        .if eax == ERROR_INVALID_HANDLE ;(6)
            print str$(eax)
            print " LastError: Invalid handle"
        .else
            print str$(eax)
            print " = LastError"
        .endif
    .endif
but the result is the same.

Quote from: dedndave on May 22, 2009, 02:20:54 PM
i might be inclined to use variable names that are slightly longer - lol
th - ThdHndl - or ThreadHandle
I agree with you and usually I do the same. This time I was lazy  :bg

Quote from: dedndave on May 22, 2009, 02:20:54 PM
to help debug the problem, display the thread handle value right before attempting to close it
you are probably trying to close a handle that isn't open (i.e. the handle is not the right value)
Nope, the variable th (the Threadhandle) is set by CreateThread and afterwards I  _never_ touched it.
So CloseHandle,th definitely tries to close the handle that CreateThread had succesfully set and no other one.

In any case, thanks for replying and the hint with GetLastError.

rgds
cobold

BogdanOntanu

Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

evlncrn8

erm, without citing the obvious, your thread hasnt exited, so that could be why you can't close the handle

dedndave

verify it.......


    print str$(th)
    invoke CloseHandle,th


sometimes, we may write code that somehow modifies a variable unexpectedly
by checking it just before the CloseHandle, you know it is the right stuff
you can take the print line out, once you know it is right
many times, you can debug code like this - easier than using a debugger
you might also want to exit the thread - lol

cobold

Quote from: BogdanOntanu on May 22, 2009, 03:34:38 PM
Do you have an ExitThread in there ?

No, I haven't! I used CloseHandle because I want the thread to run infinite. It should terminate ONLY WHEN main wants, and I suppose ExitThread can only be used in the thread proc itself, right?

dedndave

try this....

    call main
    ; ---------------------------------------------------
    ; whenever th == 4 -> ERROR_INVALID_HANDLE is returned
    ; if th != 4 everything is okay
    ; don't understand because CreateThread gave handle 4
    ; without error, but CloseHandle fails????!!!
    ; ---------------------------------------------------
    invoke TerminateThread,th,NULL
    .if eax==NULL
        invoke GetLastError
        push eax
        print "Fehler CloseHandle "
        print str$(th),13,10
        pop eax

dedndave


_updloop:
    invoke SleepEx,5000,TRUE
    invoke GetTickCount
    sub eax,msec
    invoke RunTime,eax
    print uqword$(c64),9
    print uhex$(dword ptr[c64+4]),":"       ;lo
    print uhex$(dword ptr[c64+0]),13,10     ;hi
    jmp _updloop
   
    ret ; <-------------- this guy is looking kinda lonesome
Update endp

drizz

1.
"print" modifies eax

invoke CreateThread,0,0,OFFSET Update,0,0,OFFSET tID
mov th,eax


2.
Be careful when using the same global var from two threads (main thread+work thread). For anything more complicated you have to use some locking mechanism, you can't access/modify it just like that.
The truth cannot be learned ... it can only be recognized.

cobold

dedndave,

you were on solid ground, right from the beginning.
The problem was that the variable "th" (the ThreadHandle) was overwritten because of my stupid programming:

    invoke CreateThread,0,0,OFFSET Update,0,0,OFFSET tID
    .if eax == NULL
        print "CreateThread Update failed!",13,10
    .else    ; CreateThread success and its return value (eax) is overwritten
        print "Thread-ID(tID) = "     ; now the print macro overwrites eax
        print str$(tID)
    .endif
; now I move wrong value to th - so CloseHandle refered to a handle that doesn't exist
    mov th,eax     ; SHOULD BE DONE IMMED AFTER invoke CreateThread
    print " Thread Handle(th) = "
    print str$(th),13,10


I knew that ExitProcess does the necessary clean up:
- closes all object handles
- terminates all threads
but I wanted to check return values for errors manually, because I think that's a good programming style.

Can you or someone who reads this help me to clarify the following questions?
About ExitThread MSDN says:
"When this function is called (either explicitly or by returning from a thread procedure),......"
Does it mean that ExitThread is called implicitly by the system when a thread procedure executes the ret instruction?

MSDN again:
"Terminating a thread does not necessarily remove the thread object from the operating system. A thread object is deleted when the last handle to the thread is closed."
Does it mean that you manually have to call CloseHandle,ThreadHandle ??
or
can I skip CloseHandle and leave this job to ExitProcess? (because MSDN: "All of the object handles opened by the process are closed.")

Thanks again for your help.

Nice weekend to all - cobold

drizz

Quote from: cobold on May 23, 2009, 02:58:20 PM
About ExitThread MSDN says:
"When this function is called (either explicitly or by returning from a thread procedure),......"
Does it mean that ExitThread is called implicitly by the system when a thread procedure executes the ret instruction?
Yes, but don't depend on it (for example your thread proc screws up the stack) use ExitThread yourself, the sameway you always use ExitProcess.

Quote from: cobold on May 23, 2009, 02:58:20 PM
MSDN again:
"Terminating a thread does not necessarily remove the thread object from the operating system. A thread object is deleted when the last handle to the thread is closed."
Does it mean that you manually have to call CloseHandle,ThreadHandle ??
or
can I skip CloseHandle and leave this job to ExitProcess? (because MSDN: "All of the object handles opened by the process are closed.")
Yes you can skip it, but you said it yourself, bad coding practice.

The truth cannot be learned ... it can only be recognized.

dedndave

QuoteAbout ExitThread MSDN says:
"When this function is called (either explicitly or by returning from a thread procedure),......"
Does it mean that ExitThread is called implicitly by the system when a thread procedure executes the ret instruction?
most threads terminate themselves - not with a ret, but with ExitThread
this is not the case with your code - it is terminated externaly with TerminateThread

Quote
MSDN again:
"Terminating a thread does not necessarily remove the thread object from the operating system. A thread object is deleted when the last handle to the thread is closed."
Does it mean that you manually have to call CloseHandle,ThreadHandle ??
or
can I skip CloseHandle and leave this job to ExitProcess? (because MSDN: "All of the object handles opened by the process are closed.")
i believe that TerminateThread closes the handle (ms docs a little shady on that one)
but
to test it, try this.......

INVOKE TerminateThread,th,NULL
INVOKE CloseHandle,th
    .if eax == ERROR_SUCCESS
            print chr$("TerminateThread did not close the handle - CloseHandle required")
        .else
            print chr$("TerminateThread did close the handle - CloseHandle not required")
        .endif

this should tell you if "th" is still an open handle

EDIT:
drizz is probably right
the best way to terminate the thread is to tell it to exit - let it do it, itself

.data
endThread dd 0

.code
main proc
.
.
;to terminate the thread
not dword ptr endThread
.
.
.
main endp

;then, in the code for the thread

update proc

ThLoop:
.
;do stuff
.
mov eax,endThread
or eax,eax
jz ThLoop

INVOKE ExitThread,NULL

update endp


cobold

dedndave,
Quote from: dedndave on May 24, 2009, 12:50:48 PM
how did that work out?
Thanks for asking.. yes it works now and I made some changes according to drizz:

drizz,
thanks for your helpful comments also.
Quote from: drizz on May 23, 2009, 02:54:53 PM
2.
Be careful when using the same global var from two threads (main thread+work thread). For anything more complicated you have to use some locking mechanism, you can't access/modify it just like that.

I think I got your point:
The thread "update" should not access Counter64 (global var) while it is changed by the main proc, right? F.ex. it might happen that "main" has not added to Counter64[4] yet, so update would display a wrong value. I suppose the solution I found is not very professional, but I think it works.
Any suggestion welcome!


.data?
    flagStop        dd ? ; TRUE if Thread should stop
    flagUse         dd ? ; TRUE if Counter64 is in use
; ... some code
.code
start:
    call main
   
    print "Waiting for thread termination...",13,10
    invoke WaitForSingleObject,ThreadHandle,INFINITE
    mov errCode,eax
    .if errCode == WAIT_ABANDONED
; some code for error testing
    .endif
   
    invoke CloseHandle,ThreadHandle
    mov errCode,eax ; Yep I'm learning: ALWAYS save return value IMMED after call!
    .if eax==NULL
        invoke GetLastError
        .if eax == ERROR_INVALID_HANDLE ;(6)
            print str$(eax)
            print " LastError: Invalid handle"
        .else
            print str$(eax)
            print " = LastError"
        .endif
    .else
        print str$(errCode)
        print " Success",13,10
    .endif
   
    exit
main proc
    invoke GetTickCount
    mov msec,eax

    mov flagStop,FALSE  ; tell thread it should run
   
    invoke CreateThread,0,0,OFFSET Update,0,0,OFFSET ThreadID
    mov ThreadHandle,eax
    .if eax == NULL
        print "CreateThread Update failed!",13,10
    .endif
    print " ThreadHandle = "
    print str$(ThreadHandle),13,10

ALIGN 4
_Counter64loop:
    mov flagUse,TRUE                ; tell the thread that Counter64 is in use
    add dword ptr Counter64[0],1
    jnc _cont
    adc dword ptr Counter64[4],0
_cont:
    mov flagUse,FALSE               ; release Counter64
    cmp dword ptr[Counter64+4],1
    jne _Counter64loop
   
printit:
    mov flagStop,TRUE   ; tell thread it should stop
    print "Ende "       
    invoke GetTickCount
    sub eax,msec
    invoke RunTime,eax
    print uqword$(Counter64)," hex = "
    print uhex$(dword ptr[Counter64+4]),":"       ; hi
    print uhex$(dword ptr[Counter64+0]),13,10     ; lo
    ret
main endp

; -------------------------------------------------------------------------
; Thread
; -------------------------------------------------------------------------
ALIGN 4
Update proc Param1:DWORD

ALIGN 4
_updloop:
    .if flagStop==TRUE
        invoke GetTickCount
        sub eax,msec
        invoke RunTime,eax
        print "STOP detected, exiting",13,10
        jmp _updend
    .endif
    invoke SleepEx,5000,TRUE
ALIGN 4
    .while flagUse == TRUE
        invoke GetTickCount
        sub eax,msec
        invoke RunTime,eax
        print " waiting",13,10
    .endw
    invoke GetTickCount
    sub eax,msec
    invoke RunTime,eax
    print uqword$(Counter64),9
    print uhex$(dword ptr[Counter64+4]),":"       ; hi
    print uhex$(dword ptr[Counter64+0]),13,10     ; lo
    jmp _updloop
ALIGN 4   
_updend:
    invoke ExitThread,0
Update endp


Output:
C:\Asm>threadtest
ThreadHandle = 2024
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,087s  waiting
        0t:00h:00m:05,107s 1498125505   00000000:594B94C1
        0t:00h:00m:10,114s 2967870296   00000000:B0E61B58
Ende    0t:00h:00m:14,641s 4294967296 hex = 00000001:00000000
Waiting for thread termination...
        0t:00h:00m:15,121s 4294967296   00000001:00000000
        0t:00h:00m:15,121s STOP detected, exiting
Thread finished - WAIT_OBJECT_0
1 Success