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:
Monitor()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   }
~Monitor()95   ~Monitor()
96   {
97     assert(m_recursion_count == 0);
98     pthread_cond_destroy(&m_cond);
99     pthread_mutex_destroy(&m_mutex);
100   }
lock()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   }
unlock()110   void unlock()
111   {
112     m_recursion_count--;
113     assert(m_recursion_count >= 0);
114     pthread_mutex_unlock(&m_mutex);
115   }
wait()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   }
signal()125   void signal()
126   {
127     assert(m_recursion_count > 0);
128     pthread_cond_signal(&m_cond);
129   }
broadcast_signal()130   void broadcast_signal()
131   {
132     assert(m_recursion_count > 0);
133     pthread_cond_broadcast(&m_cond);
134   }
is_locked_by_self()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:
ScopedLock(Monitor & m)158   ScopedLock(Monitor& m)
159     : m_monitor(m)
160     , m_locked(false)
161   { lock(); }
~ScopedLock()162   ~ScopedLock()
163   { if (m_locked) unlock(); }
lock()164   void lock()
165   { assert(! m_locked); m_monitor.lock(); m_locked = true; }
unlock()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:
StateVariable()181   StateVariable()
182     : m_state()
183   { }
get()184   int get()
185   {
186     ScopedLock sl(m_monitor);
187     return m_state;
188   }
set(const int state)189   void set(const int state)
190   {
191     ScopedLock sl(m_monitor);
192     m_state = state;
193     m_monitor.signal();
194   }
wait(const int state)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 
thread_func(void *)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 
main(int,char **)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