1 /* 2 * Copyright Andrey Semashev 2016. 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 /*! 8 * \file posix/ipc_sync_wrappers.hpp 9 * \author Andrey Semashev 10 * \date 05.01.2016 11 * 12 * \brief This header is the Boost.Log library implementation, see the library documentation 13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. 14 */ 15 16 #ifndef BOOST_LOG_POSIX_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ 17 #define BOOST_LOG_POSIX_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ 18 19 #include <boost/log/detail/config.hpp> 20 #include <pthread.h> 21 #include <cerrno> 22 #include <cstddef> 23 #include <boost/assert.hpp> 24 #include <boost/throw_exception.hpp> 25 // Use Boost.Interprocess to detect if process-shared pthread primitives are supported 26 #include <boost/interprocess/detail/workaround.hpp> 27 #if !defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) 28 #include <boost/core/explicit_operator_bool.hpp> 29 #include <boost/interprocess/sync/interprocess_mutex.hpp> 30 #include <boost/interprocess/sync/interprocess_condition.hpp> 31 #undef BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST 32 #endif 33 #include <boost/log/exceptions.hpp> 34 #include <boost/log/detail/header.hpp> 35 36 namespace boost { 37 38 BOOST_LOG_OPEN_NAMESPACE 39 40 namespace ipc { 41 42 namespace aux { 43 44 #if defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) 45 46 #if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) 47 struct BOOST_SYMBOL_VISIBLE lock_owner_dead {}; 48 #endif 49 50 //! Pthread mutex attributes 51 struct pthread_mutex_attributes 52 { 53 pthread_mutexattr_t attrs; 54 pthread_mutex_attributesboost::ipc::aux::pthread_mutex_attributes55 pthread_mutex_attributes() 56 { 57 int err = pthread_mutexattr_init(&this->attrs); 58 if (BOOST_UNLIKELY(err != 0)) 59 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread mutex attributes", (err)); 60 } 61 ~pthread_mutex_attributesboost::ipc::aux::pthread_mutex_attributes62 ~pthread_mutex_attributes() 63 { 64 BOOST_VERIFY(pthread_mutexattr_destroy(&this->attrs) == 0); 65 } 66 67 BOOST_DELETED_FUNCTION(pthread_mutex_attributes(pthread_mutex_attributes const&)) 68 BOOST_DELETED_FUNCTION(pthread_mutex_attributes& operator=(pthread_mutex_attributes const&)) 69 }; 70 71 //! Pthread condifion variable attributes 72 struct pthread_condition_variable_attributes 73 { 74 pthread_condattr_t attrs; 75 pthread_condition_variable_attributesboost::ipc::aux::pthread_condition_variable_attributes76 pthread_condition_variable_attributes() 77 { 78 int err = pthread_condattr_init(&this->attrs); 79 if (BOOST_UNLIKELY(err != 0)) 80 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread condition variable attributes", (err)); 81 } 82 ~pthread_condition_variable_attributesboost::ipc::aux::pthread_condition_variable_attributes83 ~pthread_condition_variable_attributes() 84 { 85 BOOST_VERIFY(pthread_condattr_destroy(&this->attrs) == 0); 86 } 87 88 BOOST_DELETED_FUNCTION(pthread_condition_variable_attributes(pthread_condition_variable_attributes const&)) 89 BOOST_DELETED_FUNCTION(pthread_condition_variable_attributes& operator=(pthread_condition_variable_attributes const&)) 90 }; 91 92 //! Interprocess mutex wrapper 93 struct interprocess_mutex 94 { 95 struct auto_unlock 96 { auto_unlockboost::ipc::aux::interprocess_mutex::auto_unlock97 explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {} ~auto_unlockboost::ipc::aux::interprocess_mutex::auto_unlock98 ~auto_unlock() { m_mutex.unlock(); } 99 100 BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&)) 101 BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&)) 102 103 private: 104 interprocess_mutex& m_mutex; 105 }; 106 107 pthread_mutex_t mutex; 108 interprocess_mutexboost::ipc::aux::interprocess_mutex109 interprocess_mutex() 110 { 111 pthread_mutex_attributes attrs; 112 int err = pthread_mutexattr_settype(&attrs.attrs, PTHREAD_MUTEX_NORMAL); 113 if (BOOST_UNLIKELY(err != 0)) 114 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to set pthread mutex type", (err)); 115 err = pthread_mutexattr_setpshared(&attrs.attrs, PTHREAD_PROCESS_SHARED); 116 if (BOOST_UNLIKELY(err != 0)) 117 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to make pthread mutex process-shared", (err)); 118 #if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) 119 err = pthread_mutexattr_setrobust(&attrs.attrs, PTHREAD_MUTEX_ROBUST); 120 if (BOOST_UNLIKELY(err != 0)) 121 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to make pthread mutex robust", (err)); 122 #endif 123 124 err = pthread_mutex_init(&this->mutex, &attrs.attrs); 125 if (BOOST_UNLIKELY(err != 0)) 126 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread mutex", (err)); 127 } 128 ~interprocess_mutexboost::ipc::aux::interprocess_mutex129 ~interprocess_mutex() 130 { 131 BOOST_VERIFY(pthread_mutex_destroy(&this->mutex) == 0); 132 } 133 lockboost::ipc::aux::interprocess_mutex134 void lock() 135 { 136 int err = pthread_mutex_lock(&this->mutex); 137 #if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) 138 if (BOOST_UNLIKELY(err == EOWNERDEAD)) 139 throw lock_owner_dead(); 140 #endif 141 if (BOOST_UNLIKELY(err != 0)) 142 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to lock pthread mutex", (err)); 143 } 144 unlockboost::ipc::aux::interprocess_mutex145 void unlock() BOOST_NOEXCEPT 146 { 147 BOOST_VERIFY(pthread_mutex_unlock(&this->mutex) == 0); 148 } 149 150 #if defined(BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST) recoverboost::ipc::aux::interprocess_mutex151 void recover() 152 { 153 int err = pthread_mutex_consistent(&this->mutex); 154 if (BOOST_UNLIKELY(err != 0)) 155 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to recover pthread mutex from a crashed thread", (err)); 156 } 157 #endif 158 159 BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&)) 160 BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&)) 161 }; 162 163 //! Interprocess condition variable wrapper 164 struct interprocess_condition_variable 165 { 166 pthread_cond_t cond; 167 interprocess_condition_variableboost::ipc::aux::interprocess_condition_variable168 interprocess_condition_variable() 169 { 170 pthread_condition_variable_attributes attrs; 171 int err = pthread_condattr_setpshared(&attrs.attrs, PTHREAD_PROCESS_SHARED); 172 if (BOOST_UNLIKELY(err != 0)) 173 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to make pthread condition variable process-shared", (err)); 174 175 err = pthread_cond_init(&this->cond, &attrs.attrs); 176 if (BOOST_UNLIKELY(err != 0)) 177 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to initialize pthread condition variable", (err)); 178 } 179 ~interprocess_condition_variableboost::ipc::aux::interprocess_condition_variable180 ~interprocess_condition_variable() 181 { 182 BOOST_VERIFY(pthread_cond_destroy(&this->cond) == 0); 183 } 184 notify_oneboost::ipc::aux::interprocess_condition_variable185 void notify_one() 186 { 187 int err = pthread_cond_signal(&this->cond); 188 if (BOOST_UNLIKELY(err != 0)) 189 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to notify one thread on a pthread condition variable", (err)); 190 } 191 notify_allboost::ipc::aux::interprocess_condition_variable192 void notify_all() 193 { 194 int err = pthread_cond_broadcast(&this->cond); 195 if (BOOST_UNLIKELY(err != 0)) 196 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to notify all threads on a pthread condition variable", (err)); 197 } 198 waitboost::ipc::aux::interprocess_condition_variable199 void wait(interprocess_mutex& mutex) 200 { 201 int err = pthread_cond_wait(&this->cond, &mutex.mutex); 202 if (BOOST_UNLIKELY(err != 0)) 203 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to wait on a pthread condition variable", (err)); 204 } 205 206 BOOST_DELETED_FUNCTION(interprocess_condition_variable(interprocess_condition_variable const&)) 207 BOOST_DELETED_FUNCTION(interprocess_condition_variable& operator=(interprocess_condition_variable const&)) 208 }; 209 210 #else // defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) 211 212 // If there are no process-shared pthread primitives, use whatever emulation Boost.Interprocess implements 213 struct interprocess_mutex 214 { 215 struct auto_unlock 216 { 217 explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {} 218 ~auto_unlock() { m_mutex.unlock(); } 219 220 BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&)) 221 BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&)) 222 223 private: 224 interprocess_mutex& m_mutex; 225 }; 226 227 BOOST_DEFAULTED_FUNCTION(interprocess_mutex(), {}) 228 229 // Members to emulate a lock interface 230 typedef boost::interprocess::interprocess_mutex mutex_type; 231 232 BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT() 233 bool operator! () const BOOST_NOEXCEPT { return false; } 234 mutex_type* mutex() BOOST_NOEXCEPT { return &m_mutex; } 235 236 void lock() 237 { 238 m_mutex.lock(); 239 } 240 241 void unlock() BOOST_NOEXCEPT 242 { 243 m_mutex.unlock(); 244 } 245 246 mutex_type m_mutex; 247 248 BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&)) 249 BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&)) 250 }; 251 252 253 typedef boost::interprocess::interprocess_condition interprocess_condition_variable; 254 255 #endif // defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) 256 257 } // namespace aux 258 259 } // namespace ipc 260 261 BOOST_LOG_CLOSE_NAMESPACE // namespace log 262 263 } // namespace boost 264 265 #include <boost/log/detail/footer.hpp> 266 267 #endif // BOOST_LOG_POSIX_IPC_SYNC_WRAPPERS_HPP_INCLUDED_ 268