1 #ifndef CORELIB___INTERPROCESS_LOCK__HPP 2 #define CORELIB___INTERPROCESS_LOCK__HPP 3 4 /* $Id: interprocess_lock.hpp 574926 2018-11-20 20:23:54Z ucko $ 5 * =========================================================================== 6 * 7 * PUBLIC DOMAIN NOTICE 8 * National Center for Biotechnology Information 9 * 10 * This software/database is a "United States Government Work" under the 11 * terms of the United States Copyright Act. It was written as part of 12 * the author's official duties as a United States Government employee and 13 * thus cannot be copyrighted. This software/database is freely available 14 * to the public for use. The National Library of Medicine and the U.S. 15 * Government have not placed any restriction on its use or reproduction. 16 * 17 * Although all reasonable efforts have been taken to ensure the accuracy 18 * and reliability of the software and data, the NLM and the U.S. 19 * Government do not and cannot warrant the performance or results that 20 * may be obtained by using this software or data. The NLM and the U.S. 21 * Government disclaim all warranties, express or implied, including 22 * warranties of performance, merchantability or fitness for any particular 23 * purpose. 24 * 25 * Please cite the author in any work or product based on this material. 26 * 27 * =========================================================================== 28 * 29 * Authors: Vladimir Ivanov, Denis Vakatov 30 * 31 * 32 */ 33 34 /// @file interprocess_lock.hpp 35 /// Simple inter-process lock. 36 /// 37 /// Defines classes: 38 /// CInterProcessLock 39 /// PInterProcessLock 40 /// CInterProcessLockException 41 42 43 #include <corelib/ncbitime.hpp> 44 #include <corelib/guard.hpp> 45 46 #if !defined(NCBI_OS_MSWIN) && !defined(NCBI_OS_UNIX) 47 # error "CInterProcessLock is not implemented on this platform" 48 #endif 49 50 51 /** @addtogroup Process 52 * 53 * @{ 54 */ 55 56 BEGIN_NCBI_SCOPE 57 58 59 ///////////////////////////////////////////////////////////////////////////// 60 /// 61 /// CInterProcessLock -- 62 /// 63 /// Simple inter-process lock. 64 /// 65 /// When the process terminates (normally or otherwise) all locks will be 66 /// removed automaticaly. On Unix, the lock file can be left in the 67 /// filesystem, but the lock itself will be removed anyway and the lock file 68 /// can be reused. 69 /// 70 /// Please consider to use class CGuard<> for locking resources in 71 /// an exception-safe manner. See also CInterProcessLock_Guard to help specify 72 /// timeouts for the locking operation when using CGuard<>. 73 /// 74 /// The lock can be safely acquired from different threads (and, with all 75 /// timeouts observed). 76 77 class NCBI_XNCBI_EXPORT CInterProcessLock 78 { 79 public: 80 /// @param name 81 /// Name of the lock. 82 /// The name must be a Unix-path like, and its length must not 83 /// exceed the maximum path length that is specific for the OS where the 84 /// application runs. 85 /// @note 86 /// This constructor will not try to acquire a lock on creation. 87 /// @note 88 /// - On Unix, if a pathless file name is given, then the corresponding 89 /// file will be created in a well-known system-specific location 90 /// (usually as '/var/tmp/<name>'); if the name represents an absolute 91 /// file path, then the exact path will be used to create the lock file. 92 /// - On Windows, no actual files will be created at the specified path. 93 /// @attention 94 /// - Relative paths are not allowed (regardless of the OS). 95 /// - Empty name is not allowed. 96 /// - Locking may not work for the files located on network filesystems 97 /// (like NFS). 98 /// @sa Lock, TryLock, GetName 99 CInterProcessLock(const string& name); 100 101 /// Call Unlock() 102 ~CInterProcessLock(void); 103 104 /// @name 105 /// Lock/guard interface (to use with CGuard<> or CInterProcessLock_Guard) 106 /// @{ 107 /// Try to acquire the lock. 108 /// On any error, or if the lock is already held by another process and 109 /// it could not be acquired within the allotted time period, 110 /// throw CInterProcessLockException. 111 /// @param timeout 112 /// How much time Lock() should wait before giving up (with error). 113 /// Default timeout value is interpreted as infinite. 114 /// @param granularity 115 /// On the systems (such as Unix) where there is no support for waiting 116 /// for the lock with a timeout, the waiting is implemented using a 117 /// loop of "try to lock" calls. This parameter specifies how much time to 118 /// wait between these calls. It will be auto-limited to the closest 119 /// whole delimiter of the 'timeout' value (and it will never exceed it). 120 /// Default or infinite granularity timeout is converted into: 121 /// MIN(timeout/5, 0.5sec). 122 /// @note 123 /// If 'timeout' is passed infinite, then the 'granularity' parameter 124 /// actually will not be used at all -- both on Windows and on Unix -- as 125 /// they both support a "honest" infinite waiting. 126 /// @note 127 /// One process can acquire the lock more than once -- but it need 128 /// to use the same CInterProcessLock object for Lock(), and then it will 129 /// need to call Unlock() as many times in order to release the lock for 130 /// other processes. 131 /// @note 132 /// If the lock object (such as file on UNIX or system mutex on Windows) 133 /// doesn't exist it will be created. 134 /// @sa TryLock, SetLockTimeout 135 void Lock(const CTimeout& timeout = CTimeout(CTimeout::eInfinite), 136 const CTimeout& granularity = CTimeout(CTimeout::eInfinite)); 137 138 /// Release the lock. 139 /// On any error (including when the lock is not held by the process), 140 /// throw CInterProcessLockException. 141 /// @sa Lock, TryLock, Remove 142 void Unlock(void); 143 /// @} 144 145 /// Try to acquire the lock. 146 /// 147 /// Acquire the lock if it can be done right away. Return immediately. 148 /// @return 149 /// - TRUE, if the lock has been successfully acquired. 150 /// - FALSE, if the lock is already held by another process 151 /// or if any error occurs. 152 /// @sa Lock 153 bool TryLock(void); 154 155 /// Call Unlock() and removes lock object from the system. 156 /// 157 /// On Unix, the Unlock() method do not remove used lock file from 158 /// the system, it just release a lock. If you don't need this file 159 /// anymore, use this method to remove it. We cannot remove it 160 /// automaticaly in the Unlock(), because on Unix locking/unlocking 161 /// is not an atomic operations and race condition is possible if 162 /// at time of deleting some other process wait to acquire a lock 163 /// using the same lock file. 164 /// @note 165 /// On Windows, it works almost as Unlock(). 166 /// @sa Unlock 167 void Remove(void); 168 169 /// Get the original name of the lock -- exactly as it was specified 170 /// in the constructor GetName(void) const171 const string& GetName(void) const { return m_Name; } 172 173 /// Get the internal name of the lock -- as it is being used in the calls 174 /// to the OS (such as '/var/tmp/<name>' rather than just '<name>' on UNIX) GetSystemName(void) const175 const string& GetSystemName(void) const { return m_SystemName; } 176 177 private: 178 /// Original name of the lock 179 string m_Name; 180 /// Adjusted name of the lock 181 string m_SystemName; 182 183 /// OS-specific lock handle 184 #if defined(NCBI_OS_UNIX) 185 typedef int TLockHandle; 186 #elif defined(NCBI_OS_MSWIN) 187 typedef HANDLE TLockHandle; 188 #endif 189 TLockHandle m_Handle; 190 }; 191 192 193 194 ///////////////////////////////////////////////////////////////////////////// 195 /// 196 /// PInterProcessLock -- 197 /// 198 /// Helper functor for CGuard<> to e.g. help specify the waiting timeout. 199 /// 200 /// @example 201 /// CInterProcessLock ipl("MyLock"); 202 /// CInterProcessLock_Guard ipl_guard 203 /// (ipl, 204 /// PInterProcessLock(3.456, 0.333)); 205 206 class PInterProcessLock 207 { 208 public: 209 typedef CInterProcessLock resource_type; 210 PInterProcessLock(const CTimeout & timeout,const CTimeout & granularity=CTimeout (CTimeout::eInfinite))211 PInterProcessLock(const CTimeout& timeout, 212 const CTimeout& granularity = CTimeout(CTimeout::eInfinite)) 213 : m_Timeout(timeout), 214 m_Granularity(granularity) 215 { 216 } 217 operator ()(resource_type & resource) const218 void operator()(resource_type& resource) const 219 { 220 resource.Lock(m_Timeout, m_Granularity); 221 } 222 223 private: 224 CTimeout m_Timeout; 225 CTimeout m_Granularity; 226 }; 227 228 229 /// Convenience typedef for PInterProcessLock 230 /// @sa PInterProcessLock 231 typedef CGuard<CInterProcessLock, PInterProcessLock> CInterProcessLock_Guard; 232 233 234 235 ///////////////////////////////////////////////////////////////////////////// 236 /// 237 /// CInterProcessLockException -- 238 /// 239 240 class NCBI_XNCBI_EXPORT CInterProcessLockException : public CCoreException 241 { 242 public: 243 /// Error types that file operations can generate. 244 enum EErrCode { 245 eLockTimeout, ///< The lock could not be acquired in the time allotted 246 eNameError, ///< Incorrect name for a lock 247 eCreateError, ///< Cannot create the lock object in the OS 248 eLockError, ///< Cannot acquire a lock (not eLockTimeout, eCreateError) 249 eUnlockError, ///< Cannot release the lock 250 eMultipleLocks, ///< Attempt to lock already locked object in the same process 251 eNotLocked ///< Attempt to unlock a not-yet-acquired lock 252 }; 253 254 /// Translate from an error code value to its string representation. 255 virtual const char* GetErrCodeString(void) const override; 256 257 // Standard exception boilerplate code. 258 NCBI_EXCEPTION_DEFAULT(CInterProcessLockException, CCoreException); 259 }; 260 261 262 END_NCBI_SCOPE 263 264 265 /* @} */ 266 267 #endif /* CORELIB___INTERPROCESS_LOCK__HPP */ 268