summaryrefslogtreecommitdiff
path: root/libs/interprocess/test/robust_mutex_test.hpp
blob: 5498b68e3067ac6a6eabf21b07667ae260c8ce88 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2010-2011. 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)
//
// See http://www.boost.org/libs/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_INTERPROCESS_TEST_ROBUST_MUTEX_TEST_HEADER
#define BOOST_INTERPROCESS_TEST_ROBUST_MUTEX_TEST_HEADER

#include <boost/interprocess/detail/config_begin.hpp>
#include <iostream>
#include <cstdlib> //std::system
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include "get_process_id_name.hpp"
#include "mutex_test_template.hpp"
#include <iostream>

namespace boost{
namespace interprocess{
namespace test{

template<class RobustMutex>
int robust_mutex_test(int argc, char *argv[])
{
   try{
   if(argc == 1){  //Parent process
      //First usual mutex tests
      {
       //  test_all_lock<RobustMutex>();
//         test_all_mutex<true, RobustMutex>();
      }
      std::cout << "robust mutex recovery test" << std::endl;

      //Remove shared memory on construction and destruction
      class shm_remove 
      {
         public:
         shm_remove(){ shared_memory_object::remove
            (::boost::interprocess::test::get_process_id_name()); }
         ~shm_remove(){ shared_memory_object::remove
            (::boost::interprocess::test::get_process_id_name()); }
      } remover;
      (void)remover;

      //Construct managed shared memory
      managed_shared_memory segment(create_only, get_process_id_name(), 65536);

      //Create two robust mutexes
      RobustMutex *instance = segment.construct<RobustMutex>
         ("robust mutex")[2]();

      //Create a flag to notify that both mutexes are
      //locked and the owner is going to die soon.
      bool *go_ahead = segment.construct<bool> ("go ahead")(false);

      //Launch child process
      std::string s(argv[0]); s += " child ";
      s += get_process_id_name();
      std::cout << "... launching child" << std::endl;
      if(0 != std::system(s.c_str()))
         return 1;

      //Wait until child locks the mutexes and dies
      while(!*go_ahead){
         ipcdetail::thread_yield();
      }
      
      std::cout << "... recovering mutex[0]" << std::endl;
      //First try to recover lock[0], put into consistent
      //state and relock it again
      {
         //Done, now try to lock it to see if robust
         //mutex recovery works
         instance[0].lock();
         if(!instance[0].previous_owner_dead())
            return 1;
         instance[0].consistent();
         instance[0].unlock();
         //Since it's consistent, locking is possible again
         instance[0].lock();
         instance[0].unlock();
      }
      //Now with lock[1], but dont' put it in consistent state
      //so the mutex is no longer usable
      std::cout << "... recovering mutex[1]" << std::endl;
      {
         //Done, now try to lock it to see if robust
         //mutex recovery works
         instance[1].lock();
         if(!instance[1].previous_owner_dead())
            return 1;
         //Unlock a recovered mutex without putting it into
         //into consistent state marks mutex as unusable.
         instance[1].unlock();
         //Since it's NOT consistent, locking is NOT possible again
         bool exception_thrown = false;
         try{
            instance[1].lock();
         }
         catch(interprocess_exception &){
            exception_thrown = true;
         }
         if(!exception_thrown){
            return 1;
         }
      }
      //Now with lock[2], this was locked by child but not
      //unlocked
      std::cout << "... recovering mutex[2]" << std::endl;
      {
         //Done, now try to lock it to see if robust
         //mutex recovery works
         instance[2].lock();
         if(!instance[2].previous_owner_dead())
            return 1;
         //Unlock a recovered mutex without putting it into
         //into consistent state marks mutex as unusable.
         instance[2].unlock();
         //Since it's NOT consistent, locking is NOT possible again
         bool exception_thrown = false;
         try{
            instance[2].lock();
         }
         catch(interprocess_exception &){
            exception_thrown = true;
         }
         if(!exception_thrown){
            return 1;
         }
      }
   }
   else{
      //Open managed shared memory
      managed_shared_memory segment(open_only, argv[2]);
      //Find mutexes
      RobustMutex *instance = segment.find<RobustMutex>("robust mutex").first;
      assert(instance);
      if(std::string(argv[1]) == std::string("child")){
         std::cout << "launched child" << std::endl;
         //Find flag
         bool *go_ahead = segment.find<bool>("go ahead").first;
         assert(go_ahead);
         //Lock, flag and die
         bool try_lock_res = instance[0].try_lock() && instance[1].try_lock();
         assert(try_lock_res);
         if(!try_lock_res)
            return 1;

         bool *go_ahead2 = segment.construct<bool>("go ahead2")(false);
         assert(go_ahead2);
         //Launch grandchild
         std::string s(argv[0]); s += " grandchild ";
         s += argv[2];
         std::cout << "... launching grandchild" << std::endl;
         if(0 != std::system(s.c_str())){
            std::cout << "launched terminated with error" << std::endl;
            return 1;
         }

         //Wait until child locks the 2nd mutex and dies
         while(!*go_ahead2){
            ipcdetail::thread_yield();
         }

         //Done, now try to lock number 3 to see if robust
         //mutex recovery works
         instance[2].lock();
         if(!instance[2].previous_owner_dead()){
            return 1;
         }
         *go_ahead = true;
      }
      else{
         std::cout << "launched grandchild" << std::endl;
         //grandchild locks the lock and dies
         bool *go_ahead2 = segment.find<bool>("go ahead2").first;
         assert(go_ahead2);
         //Lock, flag and die
         bool try_lock_res = instance[2].try_lock();
         assert(try_lock_res);
         if(!try_lock_res){
            return 1;
         }
         *go_ahead2 = true;
      }
   }
   }catch(...){
      std::cout << "Exception thrown error!" << std::endl;
      throw;
   }
   return 0;
}

}  //namespace test{
}  //namespace interprocess{
}  //namespace boost{

#include <boost/interprocess/detail/config_end.hpp>

#endif   //BOOST_INTERPROCESS_TEST_ROBUST_EMULATION_TEST_HEADER