If you have a program, that has many threads that access the same dynamic arrays of data, you need some mutex or CriticalSection. But mutex is slow- it has to enter global memory, and search for a string, specifying your mutex, and you have to wait for all. CriticalSections seem good, but accessing arrays of one million objects, and if you need to put an mutex in each of them, it all turns crap, as Andy Dick says :).
So, the easiest solution is this:


MyObject struct
IsAccessed db ?
MyData dd ? ;.....any data you want, you can have the structure as big as you want
MyObject ends


OnNewData proc me ; this is pointer to a MyObject structure
mov ebx,me
.while [ebx].MyObject.IsAccessed
push ebx
call SwitchToThread ; it is useless to do anything else!
pop ebx
.endw
mov [ebx].MyObject.IsAccessed,1

;.... do our processing on MyData here



mov ebx,me
mov [ebx].MyObject.IsAccessed,0 ; release the object

call SwitchToThread ; if this procedure is usually called by a low-priority thread, you should do this, to give immediate access to threads that are more important! Otherwise delete this line

ret
OnNewData endp



Yet, you can use only one bit to specify that the data is locked:




MyObject struct
Status db ?
MyData dd ? ;.....any data you want, you can have the structure as big as you want
MyObject ends

MY_OBJECT_STATUS_LOCKED equ 128

OnNewData proc me ; this is pointer to a MyObject structure
mov ebx,me
.while [ebx].MyObject.Status & MY_OBJECT_STATUS_LOCKED
push ebx
call SwitchToThread ; it is useless to do anything else!
pop ebx
.endw
or [ebx].MyObject.Status,MY_OBJECT_STATUS_LOCKED

;.... do our processing on MyData here



mov ebx,me
xor [ebx].MyObject.Status,MY_OBJECT_STATUS_LOCKED ; release the object

; you can use also:

;-------\
; and [ebx].MyObject.Status,( -1 - MY_OBJECT_STATUS_LOCKED)
;-------/

call SwitchToThread
ret

OnNewData endp

Posted on 2003-01-31 22:02:45 by Ultrano
A lot of 286 code...
It will be better to consider bitRAKE's solution here:
http://www.asmcommunity.net/board/showthread.php?threadid=8796&perpage=15&highlight=xchg%20bitRAKE&pagenumber=1

Regards,
Lingo
Posted on 2003-02-01 05:38:49 by lingo12
bitRake's algo is not good if the program knows the pointer, and has to save its data. For example, imagine some object that has a dynamic array with 700 objects, and it's being told to save the data into a file. So, xchg will make the program skip some important data. Or if you have a window with custom-drawn and managed windows inside (that do not use win API to work, but are managed by your program). You click with the mouse on this window over the area that is taken from virtual window Head (for instance). The program searches which window is in the Mouse_x::Mouse_y position, willing to send him a message that the mouse is down. The main window has its own array, where the children windows are registered, and if it doesn't find the target window, it does nothing. Imagine if that window is working in the moment the mouse button is pressed and we've deleted the window's pointer (unregister it). Nothing happens. Won't the user hate it?
So, if the data that we have will be saved, we should wait for the object to be released instead of skipping it ;). And with thread priorities set up, we can know which thread will be the first to switch to. I know it not from theory, but from a lot of practise. I've got a huge mainframe created using these mutex of mine, that works perfectly.
Posted on 2003-02-01 07:07:24 by Ultrano
Have you thought of what would happend if two threads came to the line:
mov .MyObject.IsAccessed,1

...at the same time? Both will get access!
Posted on 2003-02-01 12:11:41 by gliptic
How it can happen in 1 processor system if threads have different priorities?
Posted on 2003-02-01 16:13:55 by The Svin
gliptic: that hasn't happened even once for one month of testing. But if you want to ensure, if you have threads with same , normal priority, you should use


_beforeloop:
.while [ebx].MyObject.IsAccessed
push ebx
call SwitchToThread
pop ebx
.endw
inc [ebx].MyObject.IsAccessed
... a little code, a pause of two cycles
.if [ebx].MyObject.IsAccessed!=1
dec [ebx].MyObject.IsAccessed
jmp _beforeloop
.endif


Thus, the two threads will absolutely never access the object at once
Posted on 2003-02-01 20:43:41 by Ultrano
And what about multiprocessor systems? ;)
Posted on 2003-02-01 22:41:02 by iblis

gliptic: that hasn't happened even once for one month of testing. But if you want to ensure, if you have threads with same

Thus, the two threads will absolutely never access the object at once


IMHO, you missed lingo12's and gliptic's point. mov does not lock the memory automatically, while xchg with memory operand does. The important part is lock prefix. Without lock prefix, gliptic's argument is the expected result, and your experience is a pure luck -- or, you locked it before calling your code and you just forgot it. Anyhow, the code presented here is MT-unsafe, even under single CPU environment.
Posted on 2003-02-02 00:55:15 by Starless
Gosh, sorry then. /me dumb . very, very dumb.
I guess I've really been lucky.
Posted on 2003-02-02 04:59:10 by Ultrano
You can combine the best of both worlds and use this:



mov eax,TRUE
.while eax
xchg [esi].IsUsed,eax
.endw
Posted on 2003-02-02 05:39:29 by Qweerdy
no! you got the good idea, but I think the code is nothing like that. I think this is it:



@@:
mov al,1
mov ebx,me
xchg al,[ebx].MyObject.IsAccessed
or al,al
jz @F
call SwitchToThread
jmp @B
@@:


(I hope I'm not wrong as usual)
Posted on 2003-02-02 20:31:48 by Ultrano
Try cmpxchg.
Posted on 2003-02-02 20:46:41 by The Svin
Ultrano, I don't really understand what the SwitchToThread proc does. My little snippet just waits until the object is available before continuing.
Posted on 2003-02-03 08:40:13 by Qweerdy
Switches to another thead. It's in fact the essence of my idea- you must not loop and try the data, because thus you just loop until the OS switches to another thead several times, and finally goes to the thread that unlocks the object. Why wait unnecessary? Immediately switch to the thead. Well, this is best for single-cpu systems only.
Posted on 2003-02-03 11:24:25 by Ultrano
SwitchToThread is NT only. what about Sleep(0) ? or are there some consequences my dull mind hasn't picked up?
Posted on 2003-02-03 11:48:40 by f0dder
f0dder, there are some consequences that my dull mind hasn't picked up. I am using Win98SE ...
Anyway, gotta test all the thread functions today. SwitchToFiber might be ok.
Ah, at least I found one thing: in Sleep ():
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution.

Aww, sorry for wasting your time with crap
Posted on 2003-02-03 21:21:20 by Ultrano
well, i was thinking whether there's something "bad" about Sleep(0) vs. SwitchToThread() in a situation like this?
Posted on 2003-02-04 01:38:22 by f0dder

no! you got the good idea, but I think the code is nothing like that. I think this is it:



@@:
mov al,1
mov ebx,me
xchg al,[ebx].MyObject.IsAccessed
or al,al
jz @F
call SwitchToThread
jmp @B
@@:


(I hope I'm not wrong as usual)



@@:
mov eax,1
mov ebx,me
cmpxchg eax,[ebx].MyObject.IsAccessed
jne @free
call SwitchToThread
jmp @B
@free:
Posted on 2003-02-04 17:01:03 by The Svin