其实没那么深奥的!
简单地说,获得Spinlock就是个判断全局变量,改全局变量的操作。(当然,实际还要处理time-out)
AcquireSpinLock
Do
nop
While lock==1
Lock=1
ReleaseSpinLock
Lock=0
当然,以上逻辑判断和赋值必须在一条指令完成,并且考虑SMP问题。
实现SpinLock 或者 ,Lock-Free算法时,需要具体的比较和交换操作(compare and swap - CAS)
// 逻辑如下.当然,要求比较和赋值在原子级别实现,否则SMP可能会有几个CPU错误地同时获得spinlock.这是我们要避免的. 这就是用lock cmpxchg8b的原因
bool TestAndSwap(int * memvalue,int TestValue,int NewValue)
{
if(*memvalue == TestValue)
{
*memvalue = NewValue;
return true;
}
else
{
return false;
}
}
P5 指令 cmpxchg8b
cmpxchg8b m64
比较m64和edx:eax组成的64bit数
if m64=edx:eax
zf=1
ecx:ebx->m64
else
zf=0
m64->edx:eax
它有什么用?
bool TestAndSwapCmpxchg8b(_int64 * memvalue,_int64 TestValue,_int64 newvalue)
{
_asm{
mov eax,LODWORD(TestValue)
mov edx,HIDWORD(TestValue)
mov ebx,LODWORD(newvalue)
mov ecx,HIDWORD(newvalue)
lock cmpxchg8b qword ptr memvalue
jz equl
}
// mem和testvalue不等
return false;
equl:
return true;
}
AcquireSpinLock(lock * lck)
{
do
{
// just null loop
}
while(!TestAndSwap(lck->value,0,1));
}
ReleaseSpinLock(lock * lck)
{
lck->value = 0;
}
类似的 x86还有其他指令也能实现test and set
比如 BTS m32,imm
测试位偏移处的值。如为1,则CF->1
如不为1,则CF->0且测试位被设置为1.这对实现Spinlock那是相当的方便啊!
事实上Win2K就是用的lock bts.
具体见Win2K source spininst.asm : KiInst_AcquireSpinLock函数实现
void demo_bset()
{
long value = 0;
_asm {
lock bts value,0
jc is_set
jmp not_set
}
is_set:
printf("is set:%X/n",value);
goto done;
not_set:
printf("not set:%X/n",value);
goto done;
done:
return ;
}
总结:
spin lock - 一个指令能实现测试和设置,都可以来实现spinlock。如果是SMP,则要加上lock 前缀。