// SubFetch(x,y) = atomically x-=y and return x (__sync_sub_and_fetch)
// AddFetch(x,y) = atomically x+=y and return x (__sync_add_and_fetch)
// CompareWait(x, y) = futex(&x, FUTEX_WAIT, y) wait on x if x == y
// Wake(x, y) = futex(&x, FUTEX_WAKE, y) wake up y waiters
struct Lock
{
Lock() : x(1) {}
void lock()
{
while (true)
{
if (SubFetch(x, 1) == 0)
return;
x = -1;
CompareWait(x, -1);
}
}
void unlock()
{
if (AddFetch(x, 1) == 1)
return;
x = 1;
Wake(x, 1);
}
private:
int x;
};
Linux 3.0 предоставляет системный вызов под названием futex , на котором основаны многие утилиты параллелизма, включая недавние реализации pthread_mutex.Всякий раз, когда вы пишете код, вы всегда должны учитывать, является ли использование существующей реализации или написание ее самостоятельно лучшим выбором для вашего проекта.
Выше приведена реализация блокировки (мьютекс, семафор разрешений 1) на основе futex иописание семантики в man futex (7)
Похоже, что оно содержит ошибку взаимоблокировки, в результате которой несколько потоков пытаются заблокировать и разблокировать его несколько тысяч раз,потоки могут войти в состояние, когда x == -1 и все потоки застряли в CompareWait, однако никто не удерживает блокировку.
Кто-нибудь может увидеть, где находится ошибка?
Обновление: Я немного удивлен, что futex (7) / семантика настолько нарушена.Я полностью переписал Lock следующим образом ... теперь это правильно?
// CompareAssign(x,y,z) atomically: if (x == y) {x = z; ret true; } else ret false;
struct Lock
{
Lock() : x(0) {}
void lock()
{
while (!CompareAssign(x, 0, 1))
if (x == 2 || CompareAssign(x, 1, 2))
CompareWait(x, 2);
}
void unlock()
{
if (SubFetch(x, 1) == 0)
return;
x = 0;
Wake(x, 1);
}
private:
int x;
};
Идея в том, что x имеет следующие три состояния:
0: unlocked
1: locked & no waiters
2: locked & waiters