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