summaryrefslogtreecommitdiff
path: root/boost/thread/testable_mutex.hpp
blob: 3c87f9349a569c629bd1f49d04e768296eacbada (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// (C) Copyright 2012 Vicente J. Botet Escriba
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)


#ifndef BOOST_THREAD_TESTABLE_LOCKABLE_HPP
#define BOOST_THREAD_TESTABLE_LOCKABLE_HPP

#include <boost/thread/detail/config.hpp>

#include <boost/thread/thread_only.hpp>

#include <boost/atomic.hpp>
#include <boost/assert.hpp>

#include <boost/config/abi_prefix.hpp>

namespace boost
{
  /**
   * Based on Associate Mutexes with Data to Prevent Races, By Herb Sutter, May 13, 2010
   * http://www.drdobbs.com/windows/associate-mutexes-with-data-to-prevent-r/224701827?pgno=3
   *
   * Make our mutex testable if it isn't already.
   *
   * Many mutex services (including boost::mutex) don't provide a way to ask,
   * "Do I already hold a lock on this mutex?"
   * Sometimes it is needed to know if a method like is_locked to be available.
   * This wrapper associates an arbitrary lockable type with a thread id that stores the ID of the thread that
   * currently holds the lockable. The thread id initially holds an invalid value that means no threads own the mutex.
   * When we acquire a lock, we set the thread id; and when we release a lock, we reset it back to its default no id state.
   *
   */
  template <typename Lockable>
  class testable_mutex
  {
    Lockable mtx_;
    atomic<thread::id> id_;
  public:
    /// the type of the wrapped lockable
    typedef Lockable lockable_type;

    /// Non copyable
    BOOST_THREAD_NO_COPYABLE(testable_mutex)

    testable_mutex() : id_(thread::id()) {}

    void lock()
    {
      BOOST_ASSERT(! is_locked_by_this_thread());
      mtx_.lock();
      id_ = this_thread::get_id();
    }

    void unlock()
    {
      BOOST_ASSERT(is_locked_by_this_thread());
      id_ = thread::id();
      mtx_.unlock();
    }

    bool try_lock()
    {
      BOOST_ASSERT(! is_locked_by_this_thread());
      if (mtx_.try_lock())
      {
        id_ = this_thread::get_id();
        return true;
      }
      else
      {
        return false;
      }
    }
#ifdef BOOST_THREAD_USES_CHRONO
    template <class Rep, class Period>
    bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
    {
      BOOST_ASSERT(! is_locked_by_this_thread());
      if (mtx_.try_lock_for(rel_time))
      {
        id_ = this_thread::get_id();
        return true;
      }
      else
      {
        return false;
      }
    }
    template <class Clock, class Duration>
    bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
    {
      BOOST_ASSERT(! is_locked_by_this_thread());
      if (mtx_.try_lock_until(abs_time))
      {
        id_ = this_thread::get_id();
        return true;
      }
      else
      {
        return false;
      }
    }
#endif

    bool is_locked_by_this_thread() const
    {
      return this_thread::get_id() == id_;
    }
    bool is_locked() const
    {
      return ! (thread::id() == id_);
    }

    thread::id get_id() const
    {
      return id_;
    }

    // todo add the shared and upgrade mutex functions
  };

  template <typename Lockable>
  struct is_testable_lockable : false_type
  {};

  template <typename Lockable>
  struct is_testable_lockable<testable_mutex<Lockable> > : true_type
  {};

//  /**
//   * Overloaded function used to check if the mutex is locked when it is testable and do nothing otherwise.
//   *
//   * This function is used usually to assert the pre-condition when the function can only be called when the mutex
//   * must be locked by the current thread.
//   */
//  template <typename Lockable>
//  bool is_locked_by_this_thread(testable_mutex<Lockable> const& mtx)
//  {
//    return mtx.is_locked();
//  }
//  template <typename Lockable>
//  bool is_locked_by_this_thread(Lockable const&)
//  {
//    return true;
//  }
}

#include <boost/config/abi_suffix.hpp>

#endif // header