1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // |
3 | // (C) Copyright Ion Gaztanaga 2010-2012. Distributed under the Boost |
4 | // Software License, Version 1.0. (See accompanying file |
5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
6 | // |
7 | // See http://www.boost.org/libs/interprocess for documentation. |
8 | // |
9 | ////////////////////////////////////////////////////////////////////////////// |
10 | |
11 | #ifndef BOOST_INTERPROCESS_TEST_ROBUST_MUTEX_TEST_HEADER |
12 | #define |
13 | |
14 | #include <boost/interprocess/detail/config_begin.hpp> |
15 | #include <iostream> |
16 | #include <cstdlib> //std::system |
17 | #include <boost/interprocess/sync/scoped_lock.hpp> |
18 | #include <boost/interprocess/managed_shared_memory.hpp> |
19 | #include <boost/interprocess/sync/spin/wait.hpp> |
20 | #include "get_process_id_name.hpp" |
21 | #include "mutex_test_template.hpp" |
22 | #include <iostream> |
23 | |
24 | namespace boost{ |
25 | namespace interprocess{ |
26 | namespace test{ |
27 | |
28 | template<class RobustMutex> |
29 | int robust_mutex_test(int argc, char *argv[]) |
30 | { |
31 | BOOST_TRY{ |
32 | if(argc == 1){ //Parent process |
33 | //First usual mutex tests |
34 | { |
35 | // test_all_lock<RobustMutex>(); |
36 | // test_all_mutex<true, RobustMutex>(); |
37 | } |
38 | std::cout << "robust mutex recovery test" << std::endl; |
39 | |
40 | //Remove shared memory on construction and destruction |
41 | class shm_remove |
42 | { |
43 | public: |
44 | shm_remove(){ shared_memory_object::remove |
45 | (filename: ::boost::interprocess::test::get_process_id_name()); } |
46 | ~shm_remove(){ shared_memory_object::remove |
47 | (filename: ::boost::interprocess::test::get_process_id_name()); } |
48 | } remover; |
49 | (void)remover; |
50 | |
51 | //Construct managed shared memory |
52 | managed_shared_memory segment(create_only, get_process_id_name(), 65536); |
53 | |
54 | //Create two robust mutexes |
55 | RobustMutex *instance = segment.construct<RobustMutex> |
56 | ("robust mutex" )[2](); |
57 | |
58 | //Create a flag to notify that both mutexes are |
59 | //locked and the owner is going to die soon. |
60 | bool *go_ahead = segment.construct<bool> (name: "go ahead" )(false); |
61 | |
62 | //Launch child process |
63 | std::string s(argv[0]); s += " child " ; |
64 | s += get_process_id_name(); |
65 | std::cout << "... launching child" << std::endl; |
66 | if(0 != std::system(command: s.c_str())) |
67 | return 1; |
68 | |
69 | //Wait until child locks the mutexes and dies |
70 | spin_wait swait; |
71 | while(!*go_ahead){ |
72 | swait.yield(); |
73 | } |
74 | |
75 | std::cout << "... recovering mutex[0]" << std::endl; |
76 | //First try to recover lock[0], put into consistent |
77 | //state and relock it again |
78 | { |
79 | //Done, now try to lock it to see if robust |
80 | //mutex recovery works |
81 | instance[0].lock(); |
82 | if(!instance[0].previous_owner_dead()) |
83 | return 1; |
84 | instance[0].consistent(); |
85 | instance[0].unlock(); |
86 | //Since it's consistent, locking is possible again |
87 | instance[0].lock(); |
88 | instance[0].unlock(); |
89 | } |
90 | //Now with lock[1], but dont' put it in consistent state |
91 | //so the mutex is no longer usable |
92 | std::cout << "... recovering mutex[1]" << std::endl; |
93 | { |
94 | //Done, now try to lock it to see if robust |
95 | //mutex recovery works |
96 | instance[1].lock(); |
97 | if(!instance[1].previous_owner_dead()) |
98 | return 1; |
99 | //Unlock a recovered mutex without putting it into |
100 | //into consistent state marks mutex as unusable. |
101 | instance[1].unlock(); |
102 | //Since it's NOT consistent, locking is NOT possible again |
103 | bool exception_thrown = false; |
104 | BOOST_TRY{ |
105 | instance[1].lock(); |
106 | } |
107 | BOOST_CATCH(interprocess_exception &){ |
108 | exception_thrown = true; |
109 | } BOOST_CATCH_END |
110 | if(!exception_thrown){ |
111 | return 1; |
112 | } |
113 | } |
114 | //Now with lock[2], this was locked by child but not |
115 | //unlocked |
116 | std::cout << "... recovering mutex[2]" << std::endl; |
117 | { |
118 | //Done, now try to lock it to see if robust |
119 | //mutex recovery works |
120 | instance[2].lock(); |
121 | if(!instance[2].previous_owner_dead()) |
122 | return 1; |
123 | //Unlock a recovered mutex without putting it into |
124 | //into consistent state marks mutex as unusable. |
125 | instance[2].unlock(); |
126 | //Since it's NOT consistent, locking is NOT possible again |
127 | bool exception_thrown = false; |
128 | BOOST_TRY{ |
129 | instance[2].lock(); |
130 | } |
131 | BOOST_CATCH(interprocess_exception &){ |
132 | exception_thrown = true; |
133 | } BOOST_CATCH_END |
134 | if(!exception_thrown){ |
135 | return 1; |
136 | } |
137 | } |
138 | } |
139 | else{ |
140 | //Open managed shared memory |
141 | managed_shared_memory segment(open_only, argv[2]); |
142 | //Find mutexes |
143 | RobustMutex *instance = segment.find<RobustMutex>("robust mutex" ).first; |
144 | assert(instance); |
145 | if(std::string(argv[1]) == std::string("child" )){ |
146 | std::cout << "launched child" << std::endl; |
147 | //Find flag |
148 | bool *go_ahead = segment.find<bool>(name: "go ahead" ).first; |
149 | assert(go_ahead); |
150 | //Lock, flag and die |
151 | bool try_lock_res = instance[0].try_lock() && instance[1].try_lock(); |
152 | assert(try_lock_res); |
153 | if(!try_lock_res) |
154 | return 1; |
155 | |
156 | bool *go_ahead2 = segment.construct<bool>(name: "go ahead2" )(false); |
157 | assert(go_ahead2); |
158 | //Launch grandchild |
159 | std::string s(argv[0]); s += " grandchild " ; |
160 | s += argv[2]; |
161 | std::cout << "... launching grandchild" << std::endl; |
162 | if(0 != std::system(command: s.c_str())){ |
163 | std::cout << "launched terminated with error" << std::endl; |
164 | return 1; |
165 | } |
166 | |
167 | //Wait until child locks the 2nd mutex and dies |
168 | spin_wait swait; |
169 | while(!*go_ahead2){ |
170 | swait.yield(); |
171 | } |
172 | |
173 | //Done, now try to lock number 3 to see if robust |
174 | //mutex recovery works |
175 | instance[2].lock(); |
176 | if(!instance[2].previous_owner_dead()){ |
177 | return 1; |
178 | } |
179 | *go_ahead = true; |
180 | } |
181 | else{ |
182 | std::cout << "launched grandchild" << std::endl; |
183 | //grandchild locks the lock and dies |
184 | bool *go_ahead2 = segment.find<bool>(name: "go ahead2" ).first; |
185 | assert(go_ahead2); |
186 | //Lock, flag and die |
187 | bool try_lock_res = instance[2].try_lock(); |
188 | assert(try_lock_res); |
189 | if(!try_lock_res){ |
190 | return 1; |
191 | } |
192 | *go_ahead2 = true; |
193 | } |
194 | } |
195 | }BOOST_CATCH(...){ |
196 | std::cout << "Exception thrown error!" << std::endl; |
197 | BOOST_RETHROW |
198 | } BOOST_CATCH_END |
199 | return 0; |
200 | } |
201 | |
202 | } //namespace test{ |
203 | } //namespace interprocess{ |
204 | } //namespace boost{ |
205 | |
206 | #include <boost/interprocess/detail/config_end.hpp> |
207 | |
208 | #endif //BOOST_INTERPROCESS_TEST_ROBUST_EMULATION_TEST_HEADER |
209 | |