There and Back Again
The plumbing for passing object instances around and invoking methods asynchronously are supplied by a pair of template classes: AsyncStateT and AsyncMethodT. Listing Three is simple class declaration:
Listing Three: The AsyncStateT class.
class MyAsyncClass: public AsyncStateT<MyAsyncClass>
{
public:
typedef AsyncMethodT<MyAsyncClass> Method;
typedef AsyncStateT<MyAsyncClass> State;
MyAsyncClass();
private:
unsigned Initialize();
unsigned Step();
unsigned Finalize();
unsigned Report();
unsigned _counter;
};
AsyncStateT implements an instance of IAsyncState, which is the interface required by IO::Queue::Post(). But before we can hand off an instance of our new class, we need to setup the method to be invoked on the worker thread. This is handled in the constructor as follows:
MyAsyncClass()
:_counter(0)
{
State();
State::SetInstance(this);
State::SetMethod(new Method(&MyAsyncClass::Initialize));
}
State::SetMethod indicates the current method to be executed. AsyncMethodT handles the plumbing of the dispatch mechanism as well, ensuring (for example) that we can't do something like:
State::SetMethod(new Method(&NotMyClass::NotMyMethod));
without the compiler making a fuss. By convention, I use signature of unsigned(T::*Impl)(); for my async methods.
Now, we can hand off an instance to the input queue. We avoid the temptation to mess around with the instance by not keeping a reference to it and passing ownership to the input queue.:
IO::Queue *pInput = Thread::GetInput(); pInput->Post(new MyAsyncClass());
The arrival of this object causes the IOCP to wake up a worker thread, which has been parked since it called pInput->Fetch.
for(;;) {
IO::IAsyncState* pState = NULL;
pInput->Fetch(&pState, INFINITE);
if(!pState)
break;
unsigned ret = pState->Dispatch();
if(ret == IO::Queue::ERROR_YIELD)
pInput->Post(pState);
}
With the retrieved pointer to an instance of IAsyncState in hand, the worker simply invokes IAsyncState::Dispatch(). AsyncStateT's implementation of Dispatch hands the call off to the currently assigned instance of IAsyncMethod. AsyncMethodT's implementation of IAsyncMethod::Dispatch is shown in Listing Four:
Listing Four: The dispatch method.
unsigned IO::IAsyncMethod::Dispatch(IO::IAsyncState* pState)
{
AsyncStateT<T>* pContext = dynamic_cast<AsyncStateT<T>*>(pState);
assert(pContext);
T* pInst = pContext->Instance();
pInst->SetContext(pContext);
unsigned ret = (pInst->*_impl)();
pInst->SetContext(NULL);
return ret;
}
First, we unpack the instance we passed in, and the combination of dynamic_cast and assert ensure that we haven't somehow managed to incorrectly map an instance of the generic IAsyncState interface. Actually, the dynamic_cast could accomplish that on its own by returning a NULL pointer that would result in an OS fault when we tried to use it, but the assert
unsigned ret = (pInst->*_impl)();
_impl is a reference to the method which we set in our class constructor (which, you may recall, was Initialize).
unsigned Initialize()
{
_counter = 10;
State::SetMethod(new Method(&MyAsyncClass::Step));
return IO::Queue::ERROR_YIELD;
}
This routine is now executing in a worker thread and we have assigned it the herculean task of setting the _counter member to 10.



