Насколько жизнеспособна идея сделать active object используя очередь QueueUserAPC?
#include <cassert>
#include <functional>
#include <utility>
#include <boost/noncopyable.hpp>
#include <Windows.h>
class ActiveObject
{
public:
ActiveObject() : terminating(new bool(false))
{
hThread = ::CreateThread(nullptr, 0, &threadFn, this, 0, nullptr);
assert(hThread != NULL);
}
~ActiveObject()
{
invoke([this]{ *terminating = true; }); // "terminating" flag will set only when queue is empty
auto waitResult = WaitForSingleObject(hThread, 42000 /* 42 sec */);
assert(waitResult == WAIT_OBJECT_0); // you can use ProcessExplorer to kill the thread, but memory shall leak.
// with some luck, thread can awake and terminate itself
CloseHandle(hThread);
}
template<typename Callable>
void invoke(Callable&& message) // replace Callable with std::function if executable will be too big
{
class Message : boost::noncopyable
{
public:
Message(HANDLE hThread, Callable&& message) : fn(message)
{
auto ok = QueueUserAPC(&apcFunc, hThread, (ULONG_PTR)this);
assert(ok != 0); ok;
}
private:
static void __stdcall apcFunc(ULONG_PTR param)
{
auto that = (Message*)param;
try
{
that->fn();
}
catch(...) // if thread will die - all messages will be lost
{
assert(!"something wrong happen");
}
delete that;
}
Callable fn;
};
new Message(hThread, std::forward<Callable>(message));
}
private:
static DWORD __stdcall threadFn(LPVOID param)
{
auto that = (ActiveObject*)param;
auto terminating = that->terminating; // we create a copy, valid even when ActiveObject will be destroyed
while(!terminating)
::SleepEx(INFINITE, TRUE);
delete terminating;
return 0;
}
HANDLE hThread;
volatile bool* terminating;
};
// ======== test code ========
#include <iostream>
int main()
{
{ // just a scope for ActiveObject
ActiveObject a;
a.invoke([]{ std::cout << "I'm working o_O" << std::endl; });
a.invoke([]{
std::cout << "Now, I'm gonna sleep for a while" << std::endl;
Sleep(2000);
});
Sleep(500);
std::cout << "Main thread works too" << std::endl;
}
std::cout << "ActiveObject died" << std::endl;
}