I am attending an OS course as part of my undergrad and I have encountered a frustrating bug that is only present when compiling with -O2/3 flags set.
System: x86
Compiler: GCC
Emulator: Bochs/Qemu
I'm maintaining critical sections using spin locks, a TTAS implementation.
static int
xchange(int*s)
{
int val = LOCKED;
/* Exchanging value at lock address with 1, returns the old value */
asm volatile("xchg (%%eax), %%ebx" : "=b"(val) : "0"(val), "a"(s));
return val;
}
void
TTAS(int *s)
{
/* While lock is locked, do nothing */
while(TRUE){
while(*s == LOCKED){}
/* If lock acquired */
if( xchange(s) == UNLOCKED){
return;
}
}
}
Now the bug occurs when the two threads work on a shared variable with a mix of conditional waits and locks. The threads believe they have acquired the lock, but a subsequent read return with the wrong(old) value. I tried wrapping the locks for printing out the last 'owner', but this added time causes the synchronization to hold. The last and current owner of the lock:Thread 2 racing itself
If I print the value of the lock after acquiring it.
TTAS(lock <int*>);
print(lock::val);
print(lock::val);
The first print '0', the second '1'.
If I swap the TTAS with a TAS, it seemingly works.
void
TAS(int *s)
{
/* While lock is locked, do nothing */
While( xchange(s) != UNLOCKED){}
}
I am unable to determine what is causing this behaviour, and hope some of you could help me with the reasoning.\
EDIT: Corrected a wrong void return on xchange
xchange
can't bevoid
- you need it to return a value. godbolt.org/z/fY3cq43cK has a version that compiles, and usesvolatile int *s
and a fixed asm statement to make it work. lwn.net/Articles/793253 / Multithreading program stuck in optimized mode but runs normally in -O0 / How can I indicate that the memory *pointed* to by an inline ASM argument may be used? – Mistymisunderstand