Vous trouverez ma mise en œuvre à https://gist.github.com/smokku/653c469d695d60be4fe8170630ba8205
L'idée est qu'il ne peut y avoir qu'un seul fil prenant la serrure pour l'écriture (futex valeur 0
), verrouillage peut être ouvert (valeur futex 1
) ou il peut y avoir beaucoup lecture des threads (valeurs de futex supérieures à 1
). Donc, les valeurs inférieures à 1
(il n'y en a qu'une seule) bloquent les lecteurs et les écrivains sur futex, et les valeurs supérieures à 1
bloquent uniquement les auteurs. Déverrouiller le fil réveille l'un des threads en attente, mais vous devez faire attention à ne pas consommer un lecteur seulement réveiller par un thread écrivain.
#define cpu_relax() __builtin_ia32_pause()
#define cmpxchg(P, O, N) __sync_val_compare_and_swap((P), (O), (N))
static unsigned _lock = 1; // read-write lock futex
const static unsigned _lock_open = 1;
const static unsigned _lock_wlocked = 0;
static void _unlock()
{
unsigned current, wanted;
do {
current = _lock;
if (current == _lock_open) return;
if (current == _lock_wlocked) {
wanted = _lock_open;
} else {
wanted = current - 1;
}
} while (cmpxchg(&_lock, current, wanted) != current);
syscall(SYS_futex, &_lock, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
}
static void _rlock()
{
unsigned current;
while ((current = _lock) == _lock_wlocked || cmpxchg(&_lock, current, current + 1) != current) {
while (syscall(SYS_futex, &_lock, FUTEX_WAIT_PRIVATE, current, NULL, NULL, 0) != 0) {
cpu_relax();
if (_lock >= _lock_open) break;
}
// will be able to acquire rlock no matter what unlock woke us
}
}
static void _wlock()
{
unsigned current;
while ((current = cmpxchg(&_lock, _lock_open, _lock_wlocked)) != _lock_open) {
while (syscall(SYS_futex, &_lock, FUTEX_WAIT_PRIVATE, current, NULL, NULL, 0) != 0) {
cpu_relax();
if (_lock == _lock_open) break;
}
if (_lock != _lock_open) {
// in rlock - won't be able to acquire lock - wake someone else
syscall(SYS_futex, &_lock, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
}
}
}
Vous ne pouvez pas créer de bloc de travail compatible POSIX en utilisant un seul futex. Veuillez définir clairement le comportement souhaité de votre rwlock. Par exemple: Doit-il supporter le verrouillage de lecture récursif? Un écrivain bloqué doit-il empêcher d'autres verrous de lecture, ou les écrivains affamés ne sont-ils pas préoccupants? Il suffirait de fournir une version documentée de l'API que vous attendez. –
Jeremy, Si vous relisiez la description de mon problème, je n'ai jamais indiqué le besoin de conformité POSIX. Je prévois une contention minimale, mais nécessite un support pour le verrouillage partagé et exclusif. –
(Continuer le commentaire précédent) N'ayant reçu aucun pointeur vers des solutions potentielles j'ai fini par lancer le mien. Ma mise en œuvre est en effet basée sur le futex. Il fournit 1, 2 et 4 variantes d'octets, prend en charge jusqu'à 63 lecteurs dans un verrou de 1 octet et 16383 lecteurs dans un verrou de 2 octets. Sur une x86 transitions non-contestées nécessitent généralement un seul cmpxchg atomique. Ma société vient d'être rachetée par IBM. Je vérifie si je peux libérer le code sous GPL. –