2
2
3
3
#include < cstring>
4
4
#include < cassert>
5
+ #include < cstdint>
5
6
#include < system_error>
6
7
#include < mutex>
8
+ #include < atomic>
7
9
8
10
#include < pthread.h>
9
11
@@ -19,33 +21,61 @@ namespace detail {
19
21
namespace sync {
20
22
21
23
class mutex {
22
- ipc::shm::handle shm_;
24
+ ipc::shm::handle *shm_ = nullptr ;
25
+ std::atomic<std::int32_t > *ref_ = nullptr ;
23
26
pthread_mutex_t *mutex_ = nullptr ;
24
27
28
+ struct curr_prog {
29
+ struct shm_data {
30
+ ipc::shm::handle shm;
31
+ std::atomic<std::int32_t > ref;
32
+
33
+ struct init {
34
+ char const *name;
35
+ std::size_t size;
36
+ };
37
+ shm_data (init arg)
38
+ : shm{arg.name , arg.size }, ref{0 } {}
39
+ };
40
+ ipc::map<ipc::string, shm_data> mutex_handles;
41
+ std::mutex lock;
42
+
43
+ static curr_prog &get () {
44
+ static curr_prog info;
45
+ return info;
46
+ }
47
+ };
48
+
25
49
pthread_mutex_t *acquire_mutex (char const *name) {
26
- if (!shm_.acquire (name, sizeof (pthread_mutex_t ))) {
27
- ipc::error (" [acquire_mutex] fail shm.acquire: %s\n " , name);
50
+ if (name == nullptr ) {
28
51
return nullptr ;
29
52
}
30
- return static_cast <pthread_mutex_t *>(shm_.get ());
53
+ auto &info = curr_prog::get ();
54
+ IPC_UNUSED_ std::lock_guard<std::mutex> guard {info.lock };
55
+ auto it = info.mutex_handles .find (name);
56
+ if (it == info.mutex_handles .end ()) {
57
+ it = curr_prog::get ().mutex_handles .emplace (name,
58
+ curr_prog::shm_data::init{name, sizeof (pthread_mutex_t )}).first ;
59
+ }
60
+ shm_ = &it->second .shm ;
61
+ ref_ = &it->second .ref ;
62
+ if (shm_ == nullptr ) {
63
+ return nullptr ;
64
+ }
65
+ return static_cast <pthread_mutex_t *>(shm_->get ());
31
66
}
32
67
33
- pthread_mutex_t *get_mutex (char const *name) {
34
- if (name == nullptr ) {
35
- return nullptr ;
68
+ template <typename F>
69
+ void release_mutex (ipc::string const &name, F &&clear) {
70
+ if (name.empty ()) return ;
71
+ IPC_UNUSED_ std::lock_guard<std::mutex> guard {curr_prog::get ().lock };
72
+ auto it = curr_prog::get ().mutex_handles .find (name);
73
+ if (it == curr_prog::get ().mutex_handles .end ()) {
74
+ return ;
36
75
}
37
- static ipc::map<ipc::string, pthread_mutex_t *> mutex_handles;
38
- static std::mutex lock;
39
- IPC_UNUSED_ std::lock_guard<std::mutex> guard {lock};
40
- auto it = mutex_handles.find (name);
41
- if (it == mutex_handles.end ()) {
42
- auto ptr = acquire_mutex (name);
43
- if (ptr != nullptr ) {
44
- mutex_handles.emplace (name, ptr);
45
- }
46
- return ptr;
76
+ if (clear ()) {
77
+ curr_prog::get ().mutex_handles .erase (it);
47
78
}
48
- return it->second ;
49
79
}
50
80
51
81
public:
@@ -62,56 +92,69 @@ class mutex {
62
92
63
93
bool valid () const noexcept {
64
94
static const char tmp[sizeof (pthread_mutex_t )] {};
65
- return (mutex_ != nullptr )
95
+ return (shm_ != nullptr ) && (ref_ != nullptr ) && ( mutex_ != nullptr )
66
96
&& (std::memcmp (tmp, mutex_, sizeof (pthread_mutex_t )) != 0 );
67
97
}
68
98
69
99
bool open (char const *name) noexcept {
70
100
close ();
71
- if ((mutex_ = get_mutex (name)) == nullptr ) {
101
+ if ((mutex_ = acquire_mutex (name)) == nullptr ) {
72
102
return false ;
73
103
}
74
- if (shm_.ref () == 1 ) {
75
- ::pthread_mutex_destroy (mutex_);
76
- auto finally = ipc::guard ([this ] { close (); }); // close when failed
77
- // init mutex
78
- int eno;
79
- pthread_mutexattr_t mutex_attr;
80
- if ((eno = ::pthread_mutexattr_init (&mutex_attr)) != 0 ) {
81
- ipc::error (" fail pthread_mutexattr_init[%d]\n " , eno);
82
- return false ;
83
- }
84
- IPC_UNUSED_ auto guard_mutex_attr = unique_ptr (&mutex_attr, ::pthread_mutexattr_destroy);
85
- if ((eno = ::pthread_mutexattr_setpshared (&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0 ) {
86
- ipc::error (" fail pthread_mutexattr_setpshared[%d]\n " , eno);
87
- return false ;
88
- }
89
- if ((eno = ::pthread_mutexattr_setrobust (&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0 ) {
90
- ipc::error (" fail pthread_mutexattr_setrobust[%d]\n " , eno);
91
- return false ;
92
- }
93
- *mutex_ = PTHREAD_MUTEX_INITIALIZER;
94
- if ((eno = ::pthread_mutex_init (mutex_, &mutex_attr)) != 0 ) {
95
- ipc::error (" fail pthread_mutex_init[%d]\n " , eno);
96
- return false ;
97
- }
98
- finally.dismiss ();
104
+ auto self_ref = ref_->fetch_add (1 , std::memory_order_relaxed);
105
+ if (shm_->ref () > 1 || self_ref > 0 ) {
106
+ return valid ();
107
+ }
108
+ ::pthread_mutex_destroy (mutex_);
109
+ auto finally = ipc::guard ([this ] { close (); }); // close when failed
110
+ // init mutex
111
+ int eno;
112
+ pthread_mutexattr_t mutex_attr;
113
+ if ((eno = ::pthread_mutexattr_init (&mutex_attr)) != 0 ) {
114
+ ipc::error (" fail pthread_mutexattr_init[%d]\n " , eno);
115
+ return false ;
116
+ }
117
+ IPC_UNUSED_ auto guard_mutex_attr = unique_ptr (&mutex_attr, ::pthread_mutexattr_destroy);
118
+ if ((eno = ::pthread_mutexattr_setpshared (&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0 ) {
119
+ ipc::error (" fail pthread_mutexattr_setpshared[%d]\n " , eno);
120
+ return false ;
121
+ }
122
+ if ((eno = ::pthread_mutexattr_setrobust (&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0 ) {
123
+ ipc::error (" fail pthread_mutexattr_setrobust[%d]\n " , eno);
124
+ return false ;
99
125
}
126
+ *mutex_ = PTHREAD_MUTEX_INITIALIZER;
127
+ if ((eno = ::pthread_mutex_init (mutex_, &mutex_attr)) != 0 ) {
128
+ ipc::error (" fail pthread_mutex_init[%d]\n " , eno);
129
+ return false ;
130
+ }
131
+ finally.dismiss ();
100
132
return valid ();
101
133
}
102
134
103
135
void close () noexcept {
104
- if (shm_.ref () == 1 ) {
105
- int eno;
106
- if ((eno = ::pthread_mutex_destroy (mutex_)) != 0 ) {
107
- ipc::error (" fail pthread_mutex_destroy[%d]\n " , eno);
108
- }
136
+ if ((ref_ != nullptr ) && (shm_ != nullptr ) && (mutex_ != nullptr )) {
137
+ if (shm_->name () != nullptr ) {
138
+ release_mutex (shm_->name (), [this ] {
139
+ auto self_ref = ref_->fetch_sub (1 , std::memory_order_relaxed);
140
+ if ((shm_->ref () <= 1 ) && (self_ref <= 1 )) {
141
+ int eno;
142
+ if ((eno = ::pthread_mutex_destroy (mutex_)) != 0 ) {
143
+ ipc::error (" fail pthread_mutex_destroy[%d]\n " , eno);
144
+ }
145
+ return true ;
146
+ }
147
+ return false ;
148
+ });
149
+ } else shm_->release ();
109
150
}
110
- shm_.release ();
151
+ shm_ = nullptr ;
152
+ ref_ = nullptr ;
111
153
mutex_ = nullptr ;
112
154
}
113
155
114
156
bool lock (std::uint64_t tm) noexcept {
157
+ if (!valid ()) return false ;
115
158
for (;;) {
116
159
auto ts = detail::make_timespec (tm);
117
160
int eno = (tm == invalid_value)
@@ -123,8 +166,8 @@ class mutex {
123
166
case ETIMEDOUT:
124
167
return false ;
125
168
case EOWNERDEAD: {
126
- if (shm_. ref () > 1 ) {
127
- shm_. sub_ref ();
169
+ if (shm_-> ref () > 1 ) {
170
+ shm_-> sub_ref ();
128
171
}
129
172
int eno2 = ::pthread_mutex_consistent (mutex_);
130
173
if (eno2 != 0 ) {
@@ -146,6 +189,7 @@ class mutex {
146
189
}
147
190
148
191
bool try_lock () noexcept (false ) {
192
+ if (!valid ()) return false ;
149
193
auto ts = detail::make_timespec (0 );
150
194
int eno = ::pthread_mutex_timedlock (mutex_, &ts);
151
195
switch (eno) {
@@ -154,8 +198,8 @@ class mutex {
154
198
case ETIMEDOUT:
155
199
return false ;
156
200
case EOWNERDEAD: {
157
- if (shm_. ref () > 1 ) {
158
- shm_. sub_ref ();
201
+ if (shm_-> ref () > 1 ) {
202
+ shm_-> sub_ref ();
159
203
}
160
204
int eno2 = ::pthread_mutex_consistent (mutex_);
161
205
if (eno2 != 0 ) {
@@ -177,6 +221,7 @@ class mutex {
177
221
}
178
222
179
223
bool unlock () noexcept {
224
+ if (!valid ()) return false ;
180
225
int eno;
181
226
if ((eno = ::pthread_mutex_unlock (mutex_)) != 0 ) {
182
227
ipc::error (" fail pthread_mutex_unlock[%d]\n " , eno);
0 commit comments