Только что случившийся со мной случай.
Есть некий объект, упрощённое описание:
typedef struct {
int status;
int status2;
void *data;
rwlock lock1;
rwlock lock2;
}
(1) status==0, status2==0, data==NULL
(2) status==0, status2==0, data!=NULL
(3) status!=0, status2==0, data==NULL
(4) status!=0, status2 [1..3], data==NULL
(5) status==0, status2==4, data!=NULL
Переходы между состояниями такие:
1->2
, 1->3
, 1->4
, 4->5
делаются под write-lock1'ом
5->2
без write-lock1'а
внутри состояния 4 может меняться status2 - без write-lock1'а
status2 меняется между 0 и 4, а так же между 1-2-3 без write-lock1'а, потому что от этих изменений не зависит поведение остальных частей кода (интересующихся status'ом и data), а ещё у status2 есть lock2, который защищает от race между разными его переключалками (то есть именно тем кодом, чья логика завязана именно на него), тут он для упрощения не показан.
И есть ещё один тред, который эти поля иногда читает (использует) и заодно проверяет на корректность структуру (люблю рассовывать ассерты по всем подходящим местам, если оно не критично по времени). В частности, там есть такой код:
READ_LOCK(obj->lock1);
if(obj->status!=0) {
.....
} else {
assert(!obj->status2 || obj->status2==4 && obj->data);
...
}
READ_UNLOCK(obj->lock1);
Так вот, этот ассерт иногда падал. Кто угадает в чём причина?
Для справки: rwlock это примерно то же самое, что мютекс, но у него два вида блокировок, строгая - на запись, ей может владеть только один тред, и нестрогая - на чтение, ей может владеть одновременно сколько угодно тредов, при условии что никто не заблокировал на запись.