Перебрал кучу дел, не вижу проблемы. Но это довольно сложно. Я подумал, что, возможно, у вас возникнут проблемы с гонками queue_not_empty / add_to_queue. Но похоже, что пост-доминирующий CAS в обоих направлениях охватывает этот случай.
CAS стоит дорого (не так дорого, как сигнал). Если вы ожидаете, что пропущенный сигнал будет обычным, я бы кодировал CAS следующим образом:
bool cas(variable, old_val, new_val) {
if (variable != old_val) return false
asm cmpxchg
}
Такие структуры без блокировки - вот то, что Jinx (продукт, над которым я работаю) очень хорошо тестирует. Так что вы можете использовать eval-лицензию для тестирования очереди без блокировки и логики оптимизации сигналов.
Редактировать: возможно, вы можете упростить эту логику.
running = false
// add item to queue – producer thread(s)
add_to_queue()
if (cas(running, false, true)) {
signal_event()
}
// Process queue, single consumer thread
reset_event()
while(1)
{
wait_for_auto_reset_event() // Preferably IOCP
for(int i = 0; i < SpinCount; ++i)
process_queue()
cas(running, true, false) // this could just be a memory barriered store of false
if(queue_not_empty())
if(cas(running, false, true))
signal_event()
}
Теперь, когда cas / signal всегда рядом друг с другом, их можно переместить в подпрограмму.