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
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
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
Do you have an ExitThread in there ?
erm, without citing the obvious, your thread hasnt exited, so that could be why you can't close the handle
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
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?
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
_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
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.
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
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.
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
how did that work out?
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
I'm not really sure what you are trying to do there :P, you should definitely use some sort of timer object.
But if you must do it that way then use Critical Section in the thread.
main:
increment var
thread:
sleep
entercs
print var
leavecs
read more here:
http://msdn.microsoft.com/en-us/library/ms686360(VS.85).aspx
Quote from: drizz on May 25, 2009, 01:26:00 PM
I'm not really sure what you are trying to do there :P, you should definitely use some sort of timer object.
....
Well I'm only trying to get some knowledge about thread-programming. So the program has no special purpose.
My idea was to
- create a simple time-comsuming task (adding up a qword in this case)
- then create simple thread-proc that displays the value of the variable and the time
So the only purpose is to get a feeling for this kind of coding, the possible exceptions and how to handle them. You are right that one would use a timer object if the purpose is to display something in intervalls. But can you user timer objects in console mode? I don't think so, because with SetTimer you need a hWnd and have to process timer messages. As far as I know, consoleapps don't have a message-loop?
Yes, when it comes to console you don't even need a thread because it doesn't need "refreshing" you can block it just fine, unlike a gui where everything freezes :)
IMO, time consuming task should be in a thread and display in main :)
For a learning task i have an idea!
code or take sorting algos quicksort, bubblesort, insertsort
put them all in their own thread, sorting a copy of a randomized array (bigger -> more time consuming).
it would be a nice way of learning how to use synchronisation apis
cheers
In the time I spent on it, I found no way to get a normal timer to work in a console app, but a Multimedia timer is no problem.
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
include \masm32\include\winmm.inc
includelib \masm32\lib\winmm.lib
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
timerId dd 0
hEvent dd 0
count dd 0
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
TimeProc proc uID:DWORD, uMsg:DWORD, dwUser:DWORD, dw1:DWORD, dw2:DWORD
.IF count == 0
print "TimeProc thread ID "
invoke GetCurrentThreadId
print ustr$(eax),13,10,13,10
.ENDIF
inc count
print ustr$(count),9
ret
TimeProc endp
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
print "timerId "
invoke timeSetEvent, 100, 1, TimeProc, 0, TIME_PERIODIC
mov timerId, eax
print ustr$(timerId),13,10
print "Program thread ID "
invoke GetCurrentThreadId
print ustr$(eax),13,10
.WHILE count < 50
.ENDW
print chr$(13,10)
print "timeKillEvent rval "
invoke timeKillEvent, timerId
print ustr$(eax),13,10,13,10
invoke CreateEvent, 0, TRUE, FALSE, NULL
mov hEvent, eax
;-------------------------------------------------------------------
; If TIME_PERIODIC is not specified, then the timer event type will
; be TIME_ONESHOT, which apparently cancels the timer after one
; event, so a subsequent timeKillEvent will return TIMERR_NOCANDO.
;-------------------------------------------------------------------
print "timerId "
invoke timeSetEvent, 3000, 1, hEvent, 0,
TIME_PERIODIC or TIME_CALLBACK_EVENT_SET
mov timerId, eax
print ustr$(timerId),13,10,13,10
;----------------------------------------------------
; Suspend program thread until MM timer event fires.
;----------------------------------------------------
print "waiting for timer event to fire...",13,10,13,10
invoke WaitForSingleObject, hEvent, INFINITE
print "timeKillEvent rval "
invoke timeKillEvent, timerId
print ustr$(eax),13,10,13,10
inkey "Press any key to exit..."
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
QuoteI can find no way to get a normal timer to work in a console app, but a Multimedia timer is no problem
huh ? :eek
that is Michael talking, right ?
that does not sound like a Michael sentance to me - lol
i see you got my pm about threading - did i peak your curiosity ?
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.
hmm, it's not exactly that, it's totally possible to use the same global variable, but you must code your thread with (in mind) the possibility that your main thread can change the value/s (by coding entire section depending of the value). otherwise you're right on the principle... when using a thread you must understand that the main thread can modify global value/s at any moment. :wink
c:\china>\masm32\bin\ml.exe /c /coff /nologo test.asm
Assembling: test.asm
test.asm(49) : error A2006: undefined symbol : TIME_CALLBACK_EVENT_SET
test.asm(49) : error A2114: INVOKE argument type mismatch : argument : 5
Press any key to continue . . .
:red
this brings up a question that i have....
i understand most of what i have read about synchronization and locking objects
i am trying to do a little syncronization between threads without using function calls, as i want a quicker response
now, this will probably draw some critisism, but that's ok - i'm used to it - lol
in days of old, this kind of thing worked rather well - i am not so sure how the pentum cache scheme will deal
thread1 proc
xor eax,eax
th1_00: cmp eax,0FFFFFFFFh
locked label dword
jnz th1_00
;sync'ed code
thread1 endp
then, in another thread, i set locked-4 to 0....
xor eax,eax
mov locked-4,eax
the question i have is this...
will the cached (queue) code kill me ?
---------- Find in Files ----------
"C:\Cygwin\usr\include\w32api\mmsystem.h"(387,9):#define TIME_CALLBACK_EVENT_SET 16
"C:\GoASM\headers\mmsystem.h"(1139,9):#define TIME_CALLBACK_EVENT_SET 0x0010
"C:\Program Files\Microsoft SDKs\Windows\v6.0\Include\MMSystem.h"(2199,9):#define TIME_CALLBACK_EVENT_SET 0x0010 /* callback is event - use SetEvent */
"C:\WinDDK\6001.18001\inc\api\mmsystem.h"(2198,9):#define TIME_CALLBACK_EVENT_SET 0x0010 /* callback is event - use SetEvent */
4 occurrences have been found in 4 files.
Output completed (2 sec consumed)
so, add this line to '\masm32\include\windows.inc' file:
TIME_CALLBACK_EVENT_SET equ 10h
c:\china>\masm32\bin\ml.exe /c /coff /Fotest.obj /nologo test.asm
c:\china>\masm32\bin\link.exe /subsystem:console /out:test.exe /nologo test.obj
c:\china>test.exe
timerId 16
Primary thread ID 12728
TimeProc thread ID 7444
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49
50 timeKillEvent rval 0
timerId 32
waiting for timer event to fire...
timeKillEvent rval 0
Press any key to exit...
c:\china>
UtillMasm,
I'm using MASM32 version 9r with SP2, and windows.inc version 1.4c BETA 20 July 2007 that I apparently got here:
http://www.masm32.com/board/index.php?topic=7677.0
And in that version of windows.inc TIME_CALLBACK_EVENT_SET is defined, and it probably is defined in all later versions.
Looking back at the version 1.30 Release 7 March 2006 that was included with MASM32 version 9r, TIME_CALLBACK_EVENT_SET is not defined, and AFAIK SP2 did not update windows.inc.
:thumbu
very good information.
i have masm32 v10r (i think that's right) - windows.inc 6-4-2008
TIME_CALLBACK_EVENT_SET equ 10h
disappointed :( no response on my self-modifying code
(hint Michael)
:U
you are good information too.
Quoteno response on my self-modifying code
Within the limits of the code that you posted I can't see any reason why it would not work, but I also can't see any reason to use self-modifying code.
Quote from: dedndave on May 26, 2009, 02:06:22 AM
the question i have is this...
will the cached (queue) code kill me ?
the cache contain always the more recently used algos, since the os alternate the threads i don't see any possible problem/conflict (but i must admit i've never used this technic)
well - the self-modifying code is slightly faster than the memory operand method, like this....
xor eax,eax
th1_00: cmp eax,ThreadLock
jnz th1_00