Summary:
If the compare part of cmpxchg fails, it writes the unexpected value
to %rax. At certain optimization levels this was causing me to hit an
incorrectly failed assertion in some thrift code. I also cleaned up the asm
statement to use named operands.
Test Plan: Added new test that fails before this diff.
Reviewed By: delong.j@fb.com
FB internal diff:
D1083222
@override-unit-failures
*/
bool cas(uint8_t compare, uint8_t newVal) {
bool out;
- asm volatile("lock; cmpxchgb %2, (%3);"
- "setz %0;"
- : "=r" (out)
+ bool memVal; // only set if the cmpxchg fails
+ asm volatile("lock; cmpxchgb %[newVal], (%[lockPtr]);"
+ "setz %[output];"
+ : [output] "=r" (out), "=a" (memVal)
: "a" (compare), // cmpxchgb constrains this to be in %al
- "q" (newVal), // Needs to be byte-accessible
- "r" (&lock_)
+ [newVal] "q" (newVal), // Needs to be byte-accessible
+ [lockPtr] "r" (&lock_)
: "memory", "flags");
return out;
}
#include "folly/SmallLocks.h"
#include <cassert>
#include <cstdio>
+#include <mutex>
#include <string>
#include <vector>
#include <pthread.h>
}
}
+struct TestClobber {
+ TestClobber() {
+ lock_.init();
+ }
+
+ void go() {
+ std::lock_guard<MicroSpinLock> g(lock_);
+ // This bug depends on gcc register allocation and is very sensitive. We
+ // have to use DCHECK instead of EXPECT_*.
+ DCHECK(!lock_.try_lock());
+ }
+
+ private:
+ MicroSpinLock lock_;
+};
+
}
TEST(SmallLocks, SpinLockCorrectness) {
}
EXPECT_EQ(val.getData(), -8);
}
+
+TEST(SmallLocks, RegClobber) {
+ TestClobber().go();
+}