Skip to content

Instantly share code, notes, and snippets.

@chenshuo
Last active April 23, 2024 10:02

Revisions

  1. chenshuo revised this gist Sep 5, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion waiter.h
    Original file line number Diff line number Diff line change
    @@ -225,7 +225,7 @@ class Waiter8 : private WaiterBase
    void signal()
    {
    signaled_ = true;
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    }

    private:
  2. chenshuo revised this gist Sep 5, 2013. 1 changed file with 24 additions and 0 deletions.
    24 changes: 24 additions & 0 deletions waiter.h
    Original file line number Diff line number Diff line change
    @@ -207,3 +207,27 @@ class Waiter7 : private WaiterBase
    bool signaled_ = false;
    };

    // Version 8: modify signaled_ without lock
    // Incorrect, data-race and could lose signal
    class Waiter8 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    while (!signaled_)
    {
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    signaled_ = true;
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    private:
    bool signaled_ = false;
    };
  3. chenshuo revised this gist Sep 3, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion waiter.h
    Original file line number Diff line number Diff line change
    @@ -31,7 +31,7 @@ class WaiterBase : boost::noncopyable
    pthread_cond_t cond_;
    };

    // Version 1: orininal from the book
    // Version 1: orininal from the book, NOT MY BOOK
    // Incorrect, could lose signal
    class Waiter1 : private WaiterBase
    {
  4. chenshuo created this gist Sep 3, 2013.
    209 changes: 209 additions & 0 deletions waiter.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,209 @@
    #include <boost/noncopyable.hpp>
    #include <pthread.h>
    #include <stdlib.h>

    // a superfluous check for pedantic people
    inline void CHECK_SUCCESS(int ret)
    {
    if (ret != 0)
    {
    abort();
    }
    }

    // Implementaion base for init-destroy mutex_ and cond_
    class WaiterBase : boost::noncopyable
    {
    protected:
    WaiterBase()
    {
    CHECK_SUCCESS(pthread_mutex_init(&mutex_, NULL));
    CHECK_SUCCESS(pthread_cond_init(&cond_, NULL));
    }

    ~WaiterBase()
    {
    CHECK_SUCCESS(pthread_mutex_destroy(&mutex_));
    CHECK_SUCCESS(pthread_cond_destroy(&cond_));
    }

    pthread_mutex_t mutex_;
    pthread_cond_t cond_;
    };

    // Version 1: orininal from the book
    // Incorrect, could lose signal
    class Waiter1 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    }
    };

    // Version 2: signal in lock
    // Incorrect, could lose signal
    class Waiter2 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }
    };

    // Version 3: add a boolean member
    // Incorrect, spurious wakeup
    class Waiter3 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    if (!signaled_)
    {
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    signaled_ = true;
    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    private:
    bool signaled_ = false;
    };

    // Version 4: wait in while-loop
    // Correct, signal before unlock
    class Waiter4 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    while (!signaled_)
    {
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    signaled_ = true;
    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    private:
    bool signaled_ = false;
    };

    // Version 5: wait in while-loop
    // Correct, signal after unlock
    class Waiter5 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    while (!signaled_)
    {
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    signaled_ = true;
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));

    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    }

    private:
    bool signaled_ = false;
    };

    // Note: version 4 is as efficient as version 5 because of "wait morphing"

    // Version 6: signal before set boolean flag
    // Correct or not?
    class Waiter6 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    while (!signaled_)
    {
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void signal()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    CHECK_SUCCESS(pthread_cond_signal(&cond_));
    signaled_ = true;
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    private:
    bool signaled_ = false;
    };

    // Version 7: broadcast to wakeup multiple waiting threads
    // Probably the best version among above.
    class Waiter7 : private WaiterBase
    {
    public:
    void wait()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    while (!signaled_)
    {
    CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    void broadcast()
    {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    signaled_ = true;
    CHECK_SUCCESS(pthread_cond_broadcast(&cond_));
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
    }

    private:
    bool signaled_ = false;
    };