diff --git a/matrix/vm_eval.cc b/matrix/vm_eval.cc index 7b2592819..8b9760ba3 100644 --- a/matrix/vm_eval.cc +++ b/matrix/vm_eval.cc @@ -323,9 +323,20 @@ ircd::m::vm::eval::operator()(const vector_view &events) // When a fault::EXISTS would not actually be revealed to the user in // any way we can elide a lot of grief by checking this here first and // skipping the event. The query path will be adequately cached anyway. - if(~(opts->warnlog | opts->errorlog) & fault::EXISTS) - if(event.event_id && m::exists(event.event_id)) + if(event.event_id && ~(opts->warnlog | opts->errorlog) & fault::EXISTS) + { + // If the event is already being evaluated, wait here until the other + // evaluation is finished. If the other was successful, the exists() + // check will skip this, otherwise we have to try again here because + // this evaluator might be using different options/credentials. + sequence::dock.wait([&event] + { + return eval::count(event.event_id) == 0; + }); + + if(m::exists(event.event_id)) continue; + } const auto status { diff --git a/matrix/vm_execute.cc b/matrix/vm_execute.cc index a80241eb2..79ce174fa 100644 --- a/matrix/vm_execute.cc +++ b/matrix/vm_execute.cc @@ -435,7 +435,16 @@ ircd::m::vm::execute_pdu(eval &eval, fault::GENERAL, "Internal room event denied from external source." }; - if(unlikely(eval::count(event_id) > 1)) + // Wait for any pending duplicate evals before proceeding. + assert(eval::count(event_id)); + sequence::dock.wait([&event_id] + { + return eval::count(event_id) <= 1; + }); + + // This branch won't be taken anymore with the above condition added; but + // leaving the code for reference right now. + if((false) && unlikely(eval::count(event_id) > 1)) throw error { fault::EXISTS, "Event is already being evaluated."