void *
QA_PHYSICAL_CHANNEL_CLASS::WriterThread(void *argv)
{
    void ** args = (void**) argv;
    QA_PHYSICAL_CHANNEL physicalChannel = (QA_PHYSICAL_CHANNEL) args[1];

    tbb::concurrent_bounded_queue<UMF_MESSAGE> *incomingQ = &(physicalChannel->writeQ);
    QA_DEVICE_WRAPPER qaDevice = (QA_DEVICE_WRAPPER) args[0];

    while (1)
    {
        UMF_MESSAGE message;
        incomingQ->pop(message);

        // Check to see if we're being torn down -- this is
        // done by passing a special message through the writeQ

        if (message == NULL)
        {
            if (!physicalChannel->uninitialized)
            {
                cerr << "QA_PHYSICAL_CHANNEL got an unexpected NULL value" << endl;
            }

            pthread_exit(0);
        }

        // The FPGA side detects NULLs inserted for alignment by looking at the
        // length field.  Having a length of 0 would break the protocol.
        ASSERTX(message->GetLength() != 0);

        // construct header
        UMF_CHUNK header = 0;
        message->EncodeHeader((unsigned char *)&header);

        qaDevice->Write(&header, sizeof(header));

        size_t n_bytes = message->ExtractBytesLeft();
        // Round up to multiple of UMF_CHUNK size
        n_bytes = (n_bytes + sizeof(UMF_CHUNK) - 1) & ~(sizeof(UMF_CHUNK) - 1);

        qaDevice->Write(message->ExtractGetRawPtr(), n_bytes);
        message->ExtractUpdateRawPtr(n_bytes);

        // de-allocate message
        delete message;

        // Flush output channel if there isn't another message ready.
        if (incomingQ->empty())
        {
            qaDevice->Flush();
        }
    }
}