I think your algorithm has one "whole". Follow the scenario:

Thread "W" begins the process of acquiring the "Write" lock. Somewhere around 
the "mov ECX,-1" it's time slice ends up.
Thread "R" begins the process of acquiring the "Read" lock. If it gets past the 
"JS @@Spin" there's nothing to stop both threads from getting the lock. What's 
worst is that once the lock is acquired by both threads, the lock gets 
threshed. When the "W" threads finishes it's job it will set the value to "0". 
When the "R" threads finishes it will actually set the value to "-1". At this 
point the lock is locked for ever... 

The "whole" comes from the fact you're using the spin lock for more than 
locking. Classic spin-lock algorithms use one fixed value for "locked" and one 
fixed value for "open", so it's guaranteed that only one thread will ever 
acquire the lock. In the locked exchange the classic spin-lock algorithm always 
writes the "LOCKED" value when it tries to acquire the lock. Next it can safely 
test for "UNLOCKED". If it didn't get "UNLOCKED" then it may safely loop on 
because it's guaranteed that it replaced "LOCKED" with "LOCKED" - so it did no 
harm.

I think you should change your algorithm to use two separate variables: one for 
the lock, one for the counter. Protect the "counter" with the "lock" so you're 
guaranteed increments work right and you don't trash a write lock.

P.S: Why didn't you go with the TMultiReadExclusiveWriteSynchronizer? Note this 
is not a recommendation to use that, I have no idea how efficient it actually 
is - I just know it's there and it does what you need.
 
--
Cosmin Prund

> -----Original Message-----
> From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On
> Behalf Of Wu Adam
> Sent: Monday, June 04, 2007 10:16 AM
> To: [email protected]
> Subject: Re: Interface reference counting threadsafety
> 
> I've written a very lightweight read-write spin lock, just for
> protecting interface instances.
> 
> I have put it in use in my program, and it seems working fine.
> But I am not sure if it really works, becuase I have not yet
> experienced this hypothetical race condition even without any
> synchronization...
> (most likely becuase I don't have a dual-core cpu...)
> 
> If anyone got some free time, please help me bug check it.
> 
> AdamWu
> 
> (*** Start of code ***)
> 
>   // Simple Multi-read Exclusive-write spin lock
>   // NOTE: This object is designed to synchronize VERY VERY small
> pieces of code.
>   //       For efficiency reason, it does not have much error
> correction
> capability.
>   // HOW IT WORKS: It basically keeps track of the number of reader
> thread by
>   //         the (positive) value of an integer; and the writer thread
> by
> setting
>   //         the integer to -1.
>   TRWSpinLock = Class
>    Private
>     rSyncInt: Integer;
> 
>    Public
>     Constructor Create;
>     Destructor Destroy; Override;
> 
>     Class Procedure ReadSync(Var SyncInt: Integer);       Overload;
> Register;
>     Class Procedure WriteSync(Var SyncInt: Integer);      Overload;
> Register;
>     Class Procedure ReadDone(Var SyncInt: Integer);       Overload;
> Register;
>     Class Procedure WriteDone(Var SyncInt: Integer);      Overload;
> Register;
>     Procedure ReadSync;       Overload; Inline;
>     Procedure WriteSync;      Overload; Inline;
>     Procedure ReadDone;       Overload; Inline;
>     Procedure WriteDone;      Overload; Inline;
>   END;
> 
>  Class Procedure TRWSpinLock.ReadSync(Var SyncInt: Integer);
>   { @SyncInt: EDX }
>   ASM
>   @@Back:
>         MOV EAX, [ SyncInt ]
>         TEST EAX, EAX
>         JS @@Spin
> 
>         MOV ECX, EAX
>         INC ECX
>    LOCK CMPXCHG [ SyncInt ], ECX
>         JZ  @@Done
> 
>   @@Spin:
>         PAUSE
>         JMP @@Back
> 
>   @@Done:
>   END;        
> 
>  Class Procedure TRWSpinLock.WriteSync(Var SyncInt: Integer);
>   { @SyncInt: EDX }
>   ASM
>   @@Back:
>         MOV EAX, [ SyncInt ]
>         TEST EAX, EAX
>         JNZ  @@Spin
> 
>         MOV ECX, -1
>    LOCK CMPXCHG [ SyncInt ], ECX
>         JZ  @@Done
> 
>   @@Spin:
>         PAUSE
>         JMP @@Back
> 
>   @@Done:
>   END;
> 
>  Class Procedure TRWSpinLock.ReadDone(Var SyncInt: Integer);
>   { @SyncInt: EDX }
>   ASM
>         MOV  EAX, -1
>    LOCK XADD [ SyncInt ], EAX
>   END;
> 
>  Class Procedure TRWSpinLock.WriteDone(Var SyncInt: Integer);
>   { @SyncInt: EDX }
>   ASM
>         XOR  EAX, EAX
>    LOCK XCHG [ SyncInt ], EAX
>   END;
> 
>  Constructor TRWSpinLock.Create;
>   BEGIN
>    Inherited;
> 
>    //rSyncInt:= 0;
>   END;
> 
>  Destructor TRWSpinLock.Destroy;
>   BEGIN
>    WriteSync;
> 
>    Inherited;
>   END;
> 
>  Procedure TRWSpinLock.ReadSync;
>   BEGIN
>    ReadSync(rSyncInt);
>   END;
> 
>  Procedure TRWSpinLock.WriteSync;
>   BEGIN
>    WriteSync(rSyncInt);
>   END;
> 
>  Procedure TRWSpinLock.ReadDone;
>   BEGIN
>    ReadDone(rSyncInt);
>   END;
> 
>  Procedure TRWSpinLock.WriteDone;
>   BEGIN
>    WriteDone(rSyncInt);
>   END;
> 
> (*** End of code ***)
> 
> To use the spinlock to protect interface instances:
> * This class has ability to sync on both enternal and internal counters
>   For external counters, it is your responsibility to initialize it to
> zero,
>   and make sure it does not get clobbered while used for sync purposes.
> 
> * Use Read__() when making new references;
> 
> Example:
> 
> TRWSpinLock.ReadSync(RWCounter);
> NewIntf:= ProtectedIntf;
> TRWSpinLock.ReadDone;
> 
> * Use Write__() when releasing references;
> 
> TRWSpinLock.WriteSync(RWCounter);
> ProtectedIntf:= NIL;
> TRWSpinLock.WriteDone;
> 
> * There are no support for transition between "read" and "write".
> * Don't EVER mix and match __Sync() and __Done() calls.
> 
> ------
> .dlrow eht htiw thgir s'lla ,nevaeh sih ni si doG
> 
> _________________________________________________________________
> 享用世界上最大的电子邮件系统― MSN Hotmail。 http://www.hotmail.com

_______________________________________________
Delphi mailing list -> [email protected]
http://www.elists.org/mailman/listinfo/delphi

Reply via email to