N>Именно. И если CF=0, то надо не идти на цикл, а считать действие lock завершённым. Поэтому jc должно идти на цикл, а противоположность (прямой ход исполнения или jnc) — выходить из цикла.
N>Но дальше второе — чтобы не задрочить кэш, шины и всю систему постоянными попытками записи, если bts или аналог сорвались, снова идут на циклическое чтение, в ожидании, пока не появится 0.
Вот сделал без ассемблера, с std::atomic_flag по стопам приведенной тут в ссылках статьи
#include <iostream>
#include <thread>
#include <atomic>
class SpinLock
{
public:
void lock()
{
while(lck.test_and_set(std::memory_order_acquire))
{}
}
void unlock()
{
lck.clear(std::memory_order_release);
}
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
//int mutex = 0;
SpinLock lock;
inline void Lock(int * flag) {
int spins_count = 0;
// __asm {
// outer:
// mov rbx,flag
// mov eax,[rbx]
// cmp eax,0
// jne outer
//
// xor ecx,ecx
// xor eax, eax
// mov rbx,flag
// loop:
// inc ecx
// lock bts [rbx], eax // lock + bts atomic test-and-set
// jnc loop
// mov spins_count,ecx
// }
lock.lock();
// locked
std::cout << "\nINSIDE_THE_LOCK:" << spins_count << "\n";
lock.unlock();
// __asm {
// mov rbx,flag
// xor eax,eax
// mov [rbx],eax
// }
}
void spinner(char * id) {
while (true) {
Lock(&mutex);
// not locked
std::cout << "·" << "SPINNER:" << id << "·";
}
}
int main() {
char * Q = "Q";
char * W = "W";
std::thread t1(spinner, Q);
std::thread t2(spinner, W);
while (true) {
// not locked
std::cout << "•";
}
}
Все равно SPINNER разрывает вывод inside the lock