static inline int // return old value
atomic_add(volatile int *count, int add)
{
#ifdef __linux__
__asm__ __volatile__(
"lock xadd %0, (%1);"
: "=a"(add)
: "r"(count), "a"(add)
: "memory"
);
#else
#error "not done yet "
#endif
return add;
}
#define nop() __asm__ ("pause" )
#define thread_yield sched_yield
inline void sched_yield()
{ Sleep(1); }
struct futex {
volatile int lock;
volatile int count;
};
#define LARGE_ENOUGH_NEGATIVE -0x7fffffff
#ifdef __cplusplus
extern "C" {
#endif
static inline void
futex_init(struct futex* pf, int count)
{
pf->lock = 0;
pf->count = count;
}
/* Return value:
* 0: okay
* ETIMEDOUT: timeout
* EINTR: interrupted
*/
static inline int
futex_sema_down(struct futex* pf, struct timespec* timeout, bool interruptable)
{
int n = atomic_add(&pf->count, -1);
if (n <= 0) {
retry:
if (0 == sys_futex(&pf->lock, FUTEX_WAIT, 0, timeout)) {
return 0;
}
switch (errno) {
case ETIMEDOUT:
atomic_add(&pf->count, 1);
return ETIMEDOUT;
case EINTR:
if (!interruptable)
goto retry;
atomic_add(&pf->count, 1);
return EINTR;
default:
RaiseError(IMPOSSIBLE__Can_not_lock_in_futex_sema_down);
}
}
return 0;
}
/* Return value:
* 1: wake up some waiter
* 0: none is waiting
*/
static inline int
futex_sema_up(struct futex* pf)
{
int retry;
int n = atomic_add(&pf->count, 1);
if (n < 0) {
retry = 10;
while (1 != (n=sys_futex(&pf->lock, FUTEX_WAKE, 1, NULL))) {
/* it means the downer decreases the count but not yet start waiting
* --- may be interrupted near the retry label in the above function;
* so we have to wait and retry.
*/
if (retry --) {
nop();
}
else {
retry = 10;
thread_yield();
}
}
return n;
}
return 0;
}
Review一位同事的代码时候看到16行这样的pause指令,颇感兴趣,关注一下用法。
- pause指令提升了自旋等待循环(spin-wait loop)的性能。当执行一个循环等待时,Intel P4或Intel Xeon处理器会因为检测到一个可能的内存顺序违规(memory order violation)而在退出循环时使性能大幅下降。PAUSE指令给处理器提了个醒:这段代码序列是个循环等待。处理器利用这个提示可以避免在大多数情况下的内存顺序违规,这将大幅提升性能。因为这个原因,所以推荐在循环等待中使用PAUSE指令。
- pause的另一个功能就是降低Intel P4在执行循环等待时的耗电量。Intel P4处理器在循环等待时会执行得非常快,这将导致处理器消耗大量的电力,而在循环中插入一个pause指令会大幅降低处理器的电力消耗。
- pause指令虽然是在Intel P4处理器开始出现的,但是它可以向后与所有的IA32处理器兼容。在早期的IA32 CPU中,pause就像NOP指令。Intel P4和Intel Xeon处理器将pause实现成一个预定义的延迟(pre-defined delay)。这种延迟是有限的,而且一些处理器可以为0。pause指令不改变处理器的架构状态(也就是说,它实际上只是执行了一个延迟——并不做任何其他事情——的操作)。