1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03
10 // UNSUPPORTED: no-threads
11 
12 // <mutex>
13 
14 // class recursive_timed_mutex;
15 
16 // void lock();
17 
18 #include <mutex>
19 #include <atomic>
20 #include <cassert>
21 #include <thread>
22 #include <vector>
23 
24 #include "make_test_thread.h"
25 
26 bool is_lockable(std::recursive_timed_mutex& m) {
27   bool did_lock;
28   std::thread t = support::make_test_thread([&] {
29     did_lock = m.try_lock();
30     if (did_lock)
31       m.unlock(); // undo side effects
32   });
33   t.join();
34 
35   return did_lock;
36 }
37 
38 int main(int, char**) {
39   // Lock a mutex that is not locked yet. This should succeed.
40   {
41     std::recursive_timed_mutex m;
42     m.lock();
43     m.unlock();
44   }
45 
46   // Lock a mutex that is already locked by this thread. This should succeed and the mutex should only
47   // be unlocked after a matching number of calls to unlock() on the same thread.
48   {
49     std::recursive_timed_mutex m;
50     int lock_count = 0;
51     for (int i = 0; i != 10; ++i) {
52       m.lock();
53       ++lock_count;
54     }
55     while (lock_count != 0) {
56       assert(!is_lockable(m));
57       m.unlock();
58       --lock_count;
59     }
60     assert(is_lockable(m));
61   }
62 
63   // Lock a mutex that is already locked by another thread. This should block until it is unlocked.
64   {
65     std::atomic<bool> ready(false);
66     std::recursive_timed_mutex m;
67     m.lock();
68     std::atomic<bool> is_locked_from_main(true);
69 
70     std::thread t = support::make_test_thread([&] {
71       ready = true;
72       m.lock();
73       assert(!is_locked_from_main);
74       m.unlock();
75     });
76 
77     while (!ready)
78       /* spin */;
79 
80     // We would rather signal this after we unlock, but that would create a race condition.
81     // We instead signal it before we unlock, which means that it's technically possible for
82     // the thread to take the lock while main is still holding it yet for the test to still pass.
83     is_locked_from_main = false;
84     m.unlock();
85 
86     t.join();
87   }
88 
89   // Make sure that at most one thread can acquire the mutex concurrently.
90   {
91     std::atomic<int> counter(0);
92     std::recursive_timed_mutex mutex;
93 
94     std::vector<std::thread> threads;
95     for (int i = 0; i != 10; ++i) {
96       threads.push_back(support::make_test_thread([&] {
97         mutex.lock();
98         counter++;
99         assert(counter == 1);
100         counter--;
101         mutex.unlock();
102       }));
103     }
104 
105     for (auto& t : threads)
106       t.join();
107   }
108 
109   return 0;
110 }
111