Home | History | Annotate | Download | only in tests
      1 /*
      2    An example that shows how to implement the monitor synchronization concept.
      3    See also http://en.wikipedia.org/wiki/Monitor_(synchronization) for more
      4    information about this concept.
      5 
      6    ----------------------------------------------------------------
      7 
      8    Notice that the following BSD-style license applies to this one
      9    file (monitor_example.cpp) only.  The rest of Valgrind is licensed
     10    under the terms of the GNU General Public License, version 2,
     11    unless otherwise indicated.  See the COPYING file in the source
     12    distribution for details.
     13 
     14    ----------------------------------------------------------------
     15 
     16    This file is part of DRD, a heavyweight Valgrind tool for detecting
     17    errors in multithreaded programs.
     18 
     19    Copyright (C) 2008-2017 Bart Van Assche. All rights reserved.
     20 
     21    Redistribution and use in source and binary forms, with or without
     22    modification, are permitted provided that the following conditions
     23    are met:
     24 
     25    1. Redistributions of source code must retain the above copyright
     26       notice, this list of conditions and the following disclaimer.
     27 
     28    2. The origin of this software must not be misrepresented; you must
     29       not claim that you wrote the original software.  If you use this
     30       software in a product, an acknowledgment in the product
     31       documentation would be appreciated but is not required.
     32 
     33    3. Altered source versions must be plainly marked as such, and must
     34       not be misrepresented as being the original software.
     35 
     36    4. The name of the author may not be used to endorse or promote
     37       products derived from this software without specific prior written
     38       permission.
     39 
     40    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     41    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     42    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     43    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     44    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     45    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     46    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     47    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     48    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     49    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     50    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     51 
     52    ----------------------------------------------------------------
     53 
     54    Notice that the above BSD-style license applies to this one
     55    file (monitor_example.cpp) only.  The rest of Valgrind is licensed
     56    under the terms of the GNU General Public License, version 2,
     57    unless otherwise indicated.  See the COPYING file in the source
     58    distribution for details.
     59 
     60    ----------------------------------------------------------------
     61 */
     62 
     63 
     64 #define _GNU_SOURCE 1
     65 
     66 
     67 #include "config.h"
     68 #include <cassert>
     69 #include <iostream>
     70 #include <pthread.h>
     71 
     72 
     73 class Monitor
     74 {
     75 public:
     76   Monitor()
     77     : m_mutex()
     78     , m_cond()
     79     , m_owner()
     80     , m_recursion_count()
     81   {
     82     pthread_mutexattr_t mutexattr;
     83     pthread_mutexattr_init(&mutexattr);
     84     pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
     85     pthread_mutex_init(&m_mutex, &mutexattr);
     86     pthread_mutexattr_destroy(&mutexattr);
     87     pthread_condattr_t condattr;
     88     pthread_condattr_init(&condattr);
     89 #if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
     90     pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
     91 #endif
     92     pthread_cond_init(&m_cond, &condattr);
     93     pthread_condattr_destroy(&condattr);
     94   }
     95   ~Monitor()
     96   {
     97     assert(m_recursion_count == 0);
     98     pthread_cond_destroy(&m_cond);
     99     pthread_mutex_destroy(&m_mutex);
    100   }
    101   void lock()
    102   {
    103     pthread_mutex_lock(&m_mutex);
    104     assert(m_recursion_count >= 0);
    105     if (++m_recursion_count == 1)
    106     {
    107       m_owner = pthread_self();
    108     }
    109   }
    110   void unlock()
    111   {
    112     m_recursion_count--;
    113     assert(m_recursion_count >= 0);
    114     pthread_mutex_unlock(&m_mutex);
    115   }
    116   void wait()
    117   {
    118     assert(m_recursion_count == 1);
    119     assert(m_owner == pthread_self());
    120     m_recursion_count--;
    121     pthread_cond_wait(&m_cond, &m_mutex);
    122     m_recursion_count++;
    123     m_owner = pthread_self();
    124   }
    125   void signal()
    126   {
    127     assert(m_recursion_count > 0);
    128     pthread_cond_signal(&m_cond);
    129   }
    130   void broadcast_signal()
    131   {
    132     assert(m_recursion_count > 0);
    133     pthread_cond_broadcast(&m_cond);
    134   }
    135   bool is_locked_by_self()
    136   {
    137     bool result;
    138     pthread_mutex_lock(&m_mutex);
    139     result = m_recursion_count > 0 && m_owner == pthread_self();
    140     pthread_mutex_unlock(&m_mutex);
    141     return result;
    142   }
    143 
    144 private:
    145   Monitor(const Monitor&);
    146   Monitor& operator=(const Monitor&);
    147 
    148   pthread_mutex_t m_mutex;
    149   pthread_cond_t  m_cond;
    150   pthread_t       m_owner;
    151   int             m_recursion_count;
    152 };
    153 
    154 
    155 class ScopedLock
    156 {
    157 public:
    158   ScopedLock(Monitor& m)
    159     : m_monitor(m)
    160     , m_locked(false)
    161   { lock(); }
    162   ~ScopedLock()
    163   { if (m_locked) unlock(); }
    164   void lock()
    165   { assert(! m_locked); m_monitor.lock(); m_locked = true; }
    166   void unlock()
    167   { assert(m_locked); m_locked = false; m_monitor.unlock(); }
    168 
    169 private:
    170   ScopedLock(const ScopedLock&);
    171   ScopedLock& operator=(const ScopedLock&);
    172 
    173   Monitor& m_monitor;
    174   bool     m_locked;
    175 };
    176 
    177 
    178 class StateVariable
    179 {
    180 public:
    181   StateVariable()
    182     : m_state()
    183   { }
    184   int get()
    185   {
    186     ScopedLock sl(m_monitor);
    187     return m_state;
    188   }
    189   void set(const int state)
    190   {
    191     ScopedLock sl(m_monitor);
    192     m_state = state;
    193     m_monitor.signal();
    194   }
    195   void wait(const int state)
    196   {
    197     ScopedLock sl(m_monitor);
    198     while (m_state != state)
    199       m_monitor.wait();
    200   }
    201 
    202 private:
    203   Monitor m_monitor;
    204   int     m_state;
    205 };
    206 
    207 
    208 static StateVariable s_sv;
    209 
    210 
    211 static void* thread_func(void*)
    212 {
    213   s_sv.wait(1);
    214   s_sv.set(2);
    215   s_sv.wait(3);
    216   s_sv.set(4);
    217   return 0;
    218 }
    219 
    220 int main(int, char**)
    221 {
    222   pthread_t tid;
    223   pthread_create(&tid, 0, thread_func, 0);
    224   s_sv.set(1);
    225   s_sv.wait(2);
    226   s_sv.set(3);
    227   s_sv.wait(4);
    228   pthread_join(tid, 0);
    229   std::cerr << "Finished successfully.\n";
    230   return 0;
    231 }
    232