Если у вас есть довольно слабые приятные свойства в отношении того, что происходит в гонках данных, это легко и просто, по сути:
/* data structure */
struct my_mutex {
sem_t sem;
volatile tid_t owner;
unsigned count;
} m;
/* lock operation */
if (m->owner == self) { // formally a race
m->cnt++;
} else {
sem_wait(&m->sem);
m->owner = self; // where self is tid of calling thread
}
/* unlock operation */
if (m->count > 0) {
m->count--;
} else {
m->owner = 0;
sem_post(&m->sem);
}
Обратите внимание, что не существует порядка, при котором вы могли бы не видеть себя каквладелец, когда вы есть, и не считайте себя владельцем, если вы не являетесь, предполагая, что гоночные чтения читают некоторую ценность из некоторого возможного заказа.Но если вы рассматриваете их как формально неопределенные, это недопустимо.
Если вы не можете этого сделать, вам обычно нужен обычный mutex + condvar для эмуляции рекурсивного мьютекса, поэтому сначала выясните, как создать condvarиз семафора.
Если ваш компилятор имеет _Atomic
и поддерживает атомарность правильного размера для идентификаторов потоков (абстрактный тип tid_t
выше; для голого металла это тип, который вы бы определили), вы можетесделайте tid_t owner
member _Atomic
и замените тест:
if (m->owner == self)
на:
if (atomic_load_explicit(&m->owner, memory_order_relaxed) == self)
и квалификатор volatile
(по большей части ненужный, но в качестве сигнала длякомпилятор, который не должен разделять или объединять нагрузки, что помогает с «слабыми хорошими свойствами», к которым я стремился в отсутствие атомарности), может быть удален.