Last time we successfully created a task and started it running.  That’s an accomplishment, but there are a couple of details we should get out of the way before we start to dive deeper into the fiber mode implementation.  Those are all on IHostTask and are Alert and Join.  Once again everything else is trivial or non-important for CoopFiber’s implementation.

 

Last time we saw a peek at Alert.  This was the m_hCurThread member variable that we set when a fiber was switched in.  Our Alert implementation uses this:

 

      if(FlagCheck(TASK_FLAG_RUNNING))

      {

            QueueUserAPC(&MyAPCProc, m_hCurThread, NULL);

      }

      FlagSet(TASK_FLAG_ALERTED,true);   

 

      return(S_OK);

 

 

Here we check if the current task is running, and if so we queue an APC to that task.  The queued APC is just a simple do-nothing function to wake the task out of its blocking operation.  Whether the task is running or not we set the alert bit. 

 

One interesting aspect of this simple sample is that if the user doesn’t schedule the task the alert can never respond.  A more complex host may choose to give priority to alerted tasks or schedule them immediately.

 

The next API to look at is the Join implementation:

 

HRESULT __stdcall CHostTask::Join(DWORD dwMilliseconds, DWORD option)

{

      DWORD result;

 

      if(option & WAIT_ALERTABLE)

      {

            result = WaitForSingleObjectEx(m_hTaskExitedEvent, dwMilliseconds, true);

      }

      else

      {

            result = WaitForSingleObject(m_hTaskExitedEvent, dwMilliseconds);

      }

 

      switch(result)   

      {

            case WAIT_OBJECT_0:

                  return(S_OK);

            case WAIT_TIMEOUT:

                  return(HOST_E_TIMEOUT);

            case WAIT_IO_COMPLETION:

                  return(HOST_E_INTERRUPTED);

      }

 

      _ASSERTE(!"Shouldn't reach here");

      return(E_FAIL);

}

 

Here we simply block the current task on an event that is set in our internal CHostTask::ExitTask API.  We’ll do an alertable wait if requested, to which the APC queued in Alert will wake us up.  Finally Join translates the result of WaitForSingleObject* into the appropriate host HRESULT.

 

We’ve mentioned ExitTask twice now (once in SwitchIn and here again) so now seems like an appropriate time to cover it.  This internal API merely calls the ICLRTask::ExitTask callback, notifying the runtime the task has exited, and updates our internal bookkeeping:

 

    if(m_pCallback!=NULL)

    {

        m_pCallback->ExitTask();   

    }

 

    SetEvent(m_hTaskExitedEvent);

   

    FlagSet(TASK_FLAG_EXITED,true);

    FlagSet(TASK_FLAG_RUNNING,false);

 

The one interesting call worth noting here is that we set the event that allows our Joined tasks to wake up.  A more complicated host would use Join as an opportunity to deschedule a fiber and would need a more sophisticated mechanism of handling exited tasks.

 

Well that wraps it up for this edition…  There’s only one more piece of unmanaged code before we start getting into the managed world.  Next time we’ll delve into the synchronization primitives.