mercredi 27 juin 2018

Lock less consumer producer using C++11

I am looking for "complete" producer-consumer sample using C++11 memory barrier.

(I have derived following example from Jeff's article and added a line to make it complete.)

void SendTestMessage(void* param)
{
    // Copy to shared memory using non-atomic stores.
    g_payload.tick  = clock();
    g_payload.str   = "TestMessage";
    g_payload.param = param;

    // Release fence.
    std::atomic_thread_fence(std::memory_order_release);

    // Perform an atomic write to indicate that the message is ready.
    g_guard.store(1, std::memory_order_relaxed);
}

bool TryReceiveMessage(Message& result)
{
    // Perform an atomic read to check whether the message is ready.
    int ready = g_guard.load(std::memory_order_relaxed);

    if (ready != 0)
    {   
        g_guard.store(0, std::memory_order_relaxed);

        // Acquire fence.
        std::atomic_thread_fence(std::memory_order_acquire);

        // Yes. Copy from shared memory using non-atomic loads.
        result.tick  = g_payload.tick;
        result.str   = g_payload.str;
        result.param = g_payload.param;

        return true;
    }

    // No.
    return false;
}

if you notice, i added "g_guard.store(0, std::memory_order_relaxed);" just before acquire. This will help following ways

  1. It would avoid "TryReciveMessage" to cosnume same message if called multiple times before new message is written
  2. It would not add explicit memory fence and hence won't affect performance
  3. since "std::memory_order_relaxed" guarantees the ordering, it would be get overwritten by "SendTestMessage" value if new load added after the "std::memory_order_acquire" is called. So, we will not miss any load.

Please provide your comments and/or suggestions.

Aucun commentaire:

Enregistrer un commentaire