I use a slightly different approach for my SpinLocks.
I want the locks to be ordered, only a single "Bus Lock" instruction, the ability to request a lock and check if the lock has been acquired, and to find out the Number of Waiting Locks.
The "TryLock" makes it possible to attempt the lock many times within a routine while continuing with code until a lock has been acquired.
I use it and check the result against [n], and if less then I Spin waiting, otherwise I continue with some other code.
This depends on the "other code", which is usually very small in cycles and all cycles counted to calculate n.
Edit: New code in the next thread. I do not need to add 2 to the Tickets any more.
Here is the code:
Code: Select all
class Packed() tSpinlock
{
private:
volatile QWORD AlignTo(8) LockTicket;
volatile QWORD AlignTo(8) LockUserTicket;
public:
FIL void Init();
FIL void Lock();
FIL QWORD NumberOfWaitingLocksRequested();
FIL QWORD TryLock(QWORD& LockTicketToWaitFor);
FIL void Unlock();
};
Code: Select all
namespace /*Spinlock*/ Kernel
{
FIL void tSpinlock::Init()
{
LockTicket = 0;
LockUserTicket = 0;
}
FIL QWORD tSpinlock::NumberOfWaitingLocksRequested() // Not guaranteed to return the correct answer, just used as a hint
{
return (LockTicket - LockUserTicket) / 2;
}
FIL QWORD tSpinlock::TryLock(QWORD& LockTicketToWaitFor) // Pass through 1 for the first lock
{
if (LockTicketToWaitFor == 1)
LockTicketToWaitFor = __sync_fetch_and_add(&LockTicket, 2);
return (LockTicketToWaitFor - LockUserTicket) / 2; // Return the number of locks before this one, 0 = Lock Acquired
}
FIL void tSpinlock::Lock()
{
QWORD LockTicketToWaitFor = __sync_fetch_and_add(&LockTicket, 2);
while (LockTicketToWaitFor != LockUserTicket)
{
// Util.Pause(); // On my servers it runs faster without Pause
}
}
FIL void tSpinlock::Unlock()
{
LockUserTicket += 2;
}
}