mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 00:16:11 +00:00
232 lines
5.9 KiB
C++
232 lines
5.9 KiB
C++
/*
|
|
An example that shows how to implement the monitor synchronization concept.
|
|
See also http://en.wikipedia.org/wiki/Monitor_(synchronization) for more
|
|
information about this concept.
|
|
|
|
----------------------------------------------------------------
|
|
|
|
Notice that the following BSD-style license applies to this one
|
|
file (monitor_example.cpp) only. The rest of Valgrind is licensed
|
|
under the terms of the GNU General Public License, version 2,
|
|
unless otherwise indicated. See the COPYING file in the source
|
|
distribution for details.
|
|
|
|
----------------------------------------------------------------
|
|
|
|
This file is part of DRD, a heavyweight Valgrind tool for detecting
|
|
errors in multithreaded programs.
|
|
|
|
Copyright (C) 2008-2009 Bart Van Assche. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. The origin of this software must not be misrepresented; you must
|
|
not claim that you wrote the original software. If you use this
|
|
software in a product, an acknowledgment in the product
|
|
documentation would be appreciated but is not required.
|
|
|
|
3. Altered source versions must be plainly marked as such, and must
|
|
not be misrepresented as being the original software.
|
|
|
|
4. The name of the author may not be used to endorse or promote
|
|
products derived from this software without specific prior written
|
|
permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
----------------------------------------------------------------
|
|
|
|
Notice that the above BSD-style license applies to this one
|
|
file (monitor_example.cpp) only. The rest of Valgrind is licensed
|
|
under the terms of the GNU General Public License, version 2,
|
|
unless otherwise indicated. See the COPYING file in the source
|
|
distribution for details.
|
|
|
|
----------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
#define _GNU_SOURCE 1
|
|
|
|
|
|
#include "config.h"
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <pthread.h>
|
|
|
|
|
|
class Monitor
|
|
{
|
|
public:
|
|
Monitor()
|
|
: m_mutex()
|
|
, m_cond()
|
|
, m_owner()
|
|
, m_recursion_count()
|
|
{
|
|
pthread_mutexattr_t mutexattr;
|
|
pthread_mutexattr_init(&mutexattr);
|
|
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
|
|
pthread_mutex_init(&m_mutex, &mutexattr);
|
|
pthread_mutexattr_destroy(&mutexattr);
|
|
pthread_condattr_t condattr;
|
|
pthread_condattr_init(&condattr);
|
|
#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
|
|
pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
|
|
#endif
|
|
pthread_cond_init(&m_cond, &condattr);
|
|
pthread_condattr_destroy(&condattr);
|
|
}
|
|
~Monitor()
|
|
{
|
|
assert(m_recursion_count == 0);
|
|
pthread_cond_destroy(&m_cond);
|
|
pthread_mutex_destroy(&m_mutex);
|
|
}
|
|
void lock()
|
|
{
|
|
pthread_mutex_lock(&m_mutex);
|
|
assert(m_recursion_count >= 0);
|
|
if (++m_recursion_count == 1)
|
|
{
|
|
m_owner = pthread_self();
|
|
}
|
|
}
|
|
void unlock()
|
|
{
|
|
m_recursion_count--;
|
|
assert(m_recursion_count >= 0);
|
|
pthread_mutex_unlock(&m_mutex);
|
|
}
|
|
void wait()
|
|
{
|
|
assert(m_recursion_count == 1);
|
|
assert(m_owner == pthread_self());
|
|
m_recursion_count--;
|
|
pthread_cond_wait(&m_cond, &m_mutex);
|
|
m_recursion_count++;
|
|
m_owner = pthread_self();
|
|
}
|
|
void signal()
|
|
{
|
|
assert(m_recursion_count > 0);
|
|
pthread_cond_signal(&m_cond);
|
|
}
|
|
void broadcast_signal()
|
|
{
|
|
assert(m_recursion_count > 0);
|
|
pthread_cond_broadcast(&m_cond);
|
|
}
|
|
bool is_locked_by_self()
|
|
{
|
|
bool result;
|
|
pthread_mutex_lock(&m_mutex);
|
|
result = m_recursion_count > 0 && m_owner == pthread_self();
|
|
pthread_mutex_unlock(&m_mutex);
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
Monitor(const Monitor&);
|
|
Monitor& operator=(const Monitor&);
|
|
|
|
pthread_mutex_t m_mutex;
|
|
pthread_cond_t m_cond;
|
|
pthread_t m_owner;
|
|
int m_recursion_count;
|
|
};
|
|
|
|
|
|
class ScopedLock
|
|
{
|
|
public:
|
|
ScopedLock(Monitor& m)
|
|
: m_monitor(m)
|
|
, m_locked(false)
|
|
{ lock(); }
|
|
~ScopedLock()
|
|
{ if (m_locked) unlock(); }
|
|
void lock()
|
|
{ assert(! m_locked); m_monitor.lock(); m_locked = true; }
|
|
void unlock()
|
|
{ assert(m_locked); m_locked = false; m_monitor.unlock(); }
|
|
|
|
private:
|
|
ScopedLock(const ScopedLock&);
|
|
ScopedLock& operator=(const ScopedLock&);
|
|
|
|
Monitor& m_monitor;
|
|
bool m_locked;
|
|
};
|
|
|
|
|
|
class StateVariable
|
|
{
|
|
public:
|
|
StateVariable()
|
|
: m_state()
|
|
{ }
|
|
int get()
|
|
{
|
|
ScopedLock sl(m_monitor);
|
|
return m_state;
|
|
}
|
|
void set(const int state)
|
|
{
|
|
ScopedLock sl(m_monitor);
|
|
m_state = state;
|
|
m_monitor.signal();
|
|
}
|
|
void wait(const int state)
|
|
{
|
|
ScopedLock sl(m_monitor);
|
|
while (m_state != state)
|
|
m_monitor.wait();
|
|
}
|
|
|
|
private:
|
|
Monitor m_monitor;
|
|
int m_state;
|
|
};
|
|
|
|
|
|
static StateVariable s_sv;
|
|
|
|
|
|
static void* thread_func(void*)
|
|
{
|
|
s_sv.wait(1);
|
|
s_sv.set(2);
|
|
s_sv.wait(3);
|
|
s_sv.set(4);
|
|
return 0;
|
|
}
|
|
|
|
int main(int, char**)
|
|
{
|
|
pthread_t tid;
|
|
pthread_create(&tid, 0, thread_func, 0);
|
|
s_sv.set(1);
|
|
s_sv.wait(2);
|
|
s_sv.set(3);
|
|
s_sv.wait(4);
|
|
pthread_join(tid, 0);
|
|
std::cerr << "Finished successfully.\n";
|
|
return 0;
|
|
}
|