TGViewer
Чайник из Юты Чайник из Юты @irrationalthings · 121 subscribers
Post #656 169
How-To: mutex в юзерспейсе, часть II

Как вы заметили, я люблю попиздеть. И ещё реже по делу, как то диктуют святые мужские традиции.

Futex и WaitOnAddress - это просто удобный механизм уходить в ожидание. Именно поэтому просто навесить какой-нибудь CAS на существующий мьютекс было идеей ниже среднего - удачи придумать, как нормально спать с примитивами, на то не рассчитанными.

The futex() system call provides a method for waiting until a certain condition becomes true. It is typically used as a blocking construct in the context of shared-memory synchronization. When using futexes, the majority of the synchronization operations are performed in user space. A user-space program employs the futex() system call only when it is likely that the program has to block for a longer time until the condition becomes true. Other futex() operations can be used to wake any processes or threads waiting for a particular condition.

То есть вызываешь FUTEX_WAIT, говоришь "за этим поинтером лежит 0хА". Если там 0хА, то поток уходит в сон. Просыпается, когда кто-то другой вызывает FUTEX_WAKE. В котором, кстати, ещё можно указывать, сколько спящих будить.

Сам мьютекс делается примерно так:
/* Simplified pthread mutex */

// 0 = unlocked,
// 1 = locked,
// 2 = locked with waiters
int mutex_word = 0;

void pthread_mutex_lock(int *mutex_word)
{
// Fast path: try to take the lock with an atomic CAS
if (atomic_cmpxchg(mutex_word, 0, 1) == 0)
return; // got it, no kernel call

// Slow path: there's contention, call into the kernel
do {
// Mark that there are waiters
atomic_set(mutex_word, 2);
// Sleep until value != 2
futex(mutex_word, FUTEX_WAIT, 2, NULL, NULL, 0);
} while (atomic_cmpxchg(mutex_word, 0, 2) != 0);
}

void pthread_mutex_unlock(int *mutex_word)
{
// Fast path: no waiters
if (atomic_xchg(mutex_word, 0) == 1)
return; // just clear, no kernel call

// Slow path: there are waiters, wake one
futex(mutex_word, FUTEX_WAKE, 1, NULL, NULL, 0);
}

В лучшем случае - мы просто дрочим mutex_word из 0 в 1, и наоборот. Потому что locking is only expensive when there's contention (это цитата а не выебон). Когда возвращаем, смотрим, не нужно ли оно кому, если нужно - будим его.

Кстати фанфакт: у фьютексов ещё есть режим с приоритетами. Если поток с высоким приоритетом спит, пока локом владеет поток с низким приоритетом, то последнему на время приоритет повышается. Чтобы съебал побыстрее.

Фокус в том, что вообще все мьютексы устроены буквально идентично. Даже в Go, хотя там они не удержались и всё-таки наебнули CAS дважды. В NetBSD, FreeBSD и Dragonfly тоже есть futex, в Винде эквивалентный WaitOnAddress - редкий момент унификации. Даже я переизобрёл свой мьютекс, когда нужно было по-честному мультиплексировать записи в сокет. Прям буквально 1 в 1.

Ну, а на правах инжирнера, существование одного фундаментального примитива приносит мне просто эстетическое удовольствие. The less the kernel knows, the better. А это уже выебон.

Но про Go я бы поговорил поподробнее. У них мьютекс интересный такой: есть starvation mode, когда кто-то дольше миллисекунды не может забрать себе лок, тогда владение мьютексом напрямую переходит к этому бедняжке. Правда, пока эта миллисекунда не прошла, происходит что-то очень близкое к busy loop. Просто потому, что запарковать горутину очень, очень дорого, а локи обычно долго не живут.

А ещё, сам sync.Mutex состоит из state int32 и sema uint32. Они двое разделяют семантику mutex_word из сниппета - state для атомарного счётчика, sema как уникальный идентификатор для рантайма. Ну, точнее, его адрес, в самом sema обычно лежит просто 0. В рантайме есть своя субсистема семафор, которая как раз и использует фьютексы и WaitOnAddress, когда можно (а когда нельзя, то фоллбэк к обычным семафорам по сисколлам). Этот sema и используется, как "якорь", на котором futex будет засыпать.
More from @irrationalthings
  1. Sep 21, 2026я хрюкнул
  2. Sep 21, 2026гемини
  3. Sep 15, 2026Тот факт, что между нейронками и компрессорами больше общего, чем может показаться - забав…
  4. Sep 15, 2026"Low-Resource" Text Classification: A Parameter-Free Classification Method with Compressor…
  5. Sep 15, 2026Конечно, они сравнивали со средненькими классифицирующими моделями. Там есть пространство…
  6. Sep 15, 2026GZIP наносит ответный удар Вот мы хотим классифицировать текст. Классическая задача для ML…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →