@@ -150,6 +150,17 @@ class mutex {
150150
151151 void close () noexcept {
152152 if ((ref_ != nullptr ) && (shm_ != nullptr ) && (mutex_ != nullptr )) {
153+ // Try to unlock the mutex before destroying it.
154+ // This is important for robust mutexes on FreeBSD, which maintain
155+ // a per-thread robust list. If we destroy a mutex while it's in
156+ // the robust list (even if not locked), FreeBSD may encounter
157+ // dangling pointers later, leading to segfaults.
158+ // We ignore any errors from unlock() since:
159+ // 1. If we don't hold the lock, EPERM is expected and harmless
160+ // 2. If the mutex is already unlocked, this is a no-op
161+ // 3. If there's an error, we still want to proceed with cleanup
162+ ::pthread_mutex_unlock (mutex_);
163+
153164 if (shm_->name () != nullptr ) {
154165 release_mutex (shm_->name (), [this ] {
155166 auto self_ref = ref_->fetch_sub (1 , std::memory_order_relaxed);
@@ -171,6 +182,9 @@ class mutex {
171182
172183 void clear () noexcept {
173184 if ((shm_ != nullptr ) && (mutex_ != nullptr )) {
185+ // Try to unlock before destroying, same reasoning as in close()
186+ ::pthread_mutex_unlock (mutex_);
187+
174188 if (shm_->name () != nullptr ) {
175189 release_mutex (shm_->name (), [this ] {
176190 int eno;
@@ -206,21 +220,17 @@ class mutex {
206220 case ETIMEDOUT:
207221 return false ;
208222 case EOWNERDEAD: {
209- if (shm_->ref () > 1 ) {
210- shm_->sub_ref ();
211- }
223+ // EOWNERDEAD means we have successfully acquired the lock,
224+ // but the previous owner died. We need to make it consistent.
212225 int eno2 = ::pthread_mutex_consistent (mutex_);
213226 if (eno2 != 0 ) {
214227 ipc::error (" fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n " , eno, eno2);
215228 return false ;
216229 }
217- int eno3 = ::pthread_mutex_unlock (mutex_);
218- if (eno3 != 0 ) {
219- ipc::error (" fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n " , eno, eno3);
220- return false ;
221- }
230+ // After calling pthread_mutex_consistent(), the mutex is now in a
231+ // consistent state and we hold the lock. Return success.
232+ return true ;
222233 }
223- break ; // loop again
224234 default :
225235 ipc::error (" fail pthread_mutex_lock[%d]\n " , eno);
226236 return false ;
@@ -238,21 +248,17 @@ class mutex {
238248 case ETIMEDOUT:
239249 return false ;
240250 case EOWNERDEAD: {
241- if (shm_->ref () > 1 ) {
242- shm_->sub_ref ();
243- }
251+ // EOWNERDEAD means we have successfully acquired the lock,
252+ // but the previous owner died. We need to make it consistent.
244253 int eno2 = ::pthread_mutex_consistent (mutex_);
245254 if (eno2 != 0 ) {
246255 ipc::error (" fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n " , eno, eno2);
247- break ;
248- }
249- int eno3 = ::pthread_mutex_unlock (mutex_);
250- if (eno3 != 0 ) {
251- ipc::error (" fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n " , eno, eno3);
252- break ;
256+ throw std::system_error{eno2, std::system_category ()};
253257 }
258+ // After calling pthread_mutex_consistent(), the mutex is now in a
259+ // consistent state and we hold the lock. Return success.
260+ return true ;
254261 }
255- break ;
256262 default :
257263 ipc::error (" fail pthread_mutex_timedlock[%d]\n " , eno);
258264 break ;
0 commit comments