Here is the final clean code of the above which is polling-free since polling technically should not be done in a waiting loop ever in modern OS code where the OS can always schedule that much more safely and efficiently:
Code:
/// Execute code in the main thread - to be used with execute_sync().
struct execFunctor : public exec_request_t
{
std::function<void()> fun;
qsemaphore_t finishSem;
int* taskNo;
execFunctor(std::function<void()> f, qsemaphore_t qs, int* tsk) : fun(f), finishSem(qs), taskNo(tsk) {}
virtual ~execFunctor(void) override {}
/// Callback to be executed.
/// If this function raises an exception, execute_sync() never returns.
virtual int idaapi execute(void)
{
fun();
*taskNo = -1;
qsem_post(finishSem);
return 0;
}
};
void IdaCallback::executeOnMainThread(std::function<void()> fun)
{
execFunctor* ef = new execFunctor(fun, di->termSem, &di->uiExecutingTask);
{ //must be a locked unit otherwise race condition can occur
qmutex_locker_t lock(di->qm);
if (di->exiting) return;
di->uiExecutingTask = execute_sync(*ef, MFF_NOWAIT);
}
qsem_wait(di->termSem, -1);
if (di->uiExecutingTask == -1) delete ef;
}
Forced termination (of course the qsem_create/qsem_free must be called at program initialization and termination):
Code:
{ //must be a locked unit otherwise race condition can occur
qmutex_locker_t lock(decompInfo->qm);
decompInfo->exiting = true;
if (decompInfo->uiExecutingTask != -1) {
if (cancel_exec_request(decompInfo->uiExecutingTask)) {
qsem_post(decompInfo->termSem);
}
}
}
Note that qwait_for_handles despite the docs not making it clear works for semaphores so you can do a multiple wait (WaitForMultipleObjects/select) e.g.:
Code:
int idx = -1;
qhandle_t handles[2] = { finishSem, termSem };
while (qwait_for_handles(&idx, handles, 2, 0, -1), idx == -1) {}