I have the following scenario:
I tried the following implementation:
void do_run()
{
while(!m_should_stop.load())
{
std::lock_guard lock{m_task_mtx};
if(m_task.step() == task_step_result::task_is_completed)
{ return; }
}
}
state get_state() const
{
std::lock_guard lock{m_task_mtx};
return m_task.current_state();
}
The problem is that get_state
never gets a chance to acquire the mutex, unless there is an artificial sleep before the lock is captured by do_run
. Could this problem be solved in some other way, such as using some priority schema. If get_state
is waiting on the resource, it should have priority over step
.
I solved the issue by injecting the request into thread 1. Basically it becomes like this
Check the mailbox, if not empty
condition_variable
)Continue with the real work
class sync_message_bus
{
public:
void operator()() // Called from Thread 1
{
std::lock_guard lock{m_mtx};
if(m_closure != nullptr) [[unlikely]]
{
m_callback(m_closure, m_result);
m_closure = nullptr;
m_cv.notify_one();
}
}
template<class Callable>
auto process(Callable&& f, bool invoke_now) // Called from Thread 2
{
if(invoke_now) // In case Thread 1 is not running
{ return std::forward<Callable>(f)(); }
using type = std::invoke_result_t<Callable>;
std::optional<type> retval;
{
std::unique_lock lock{m_mtx};
m_closure = &f;
m_callback = [](void* closure, void* result) {
auto& f = *static_cast<Callable*>(closure);
auto& retval = *static_cast<std::optional<type>*>(result);
retval = f();
};
m_result = &retval;
m_cv.wait(lock, [&retval](){
return retval.has_value();
});
}
return std::move(*retval);
}
private:
std::mutex m_mtx;
void* m_closure{nullptr};
void (*m_callback)(void* value, void* result){nullptr};
void* m_result{nullptr};
std::condition_variable m_cv;
};