void CInterProcessLock::Unlock() { if (m_Handle == kInvalidLockHandle) { NCBI_THROW(CInterProcessLockException, eNotLocked, "Attempt to unlock not-yet-acquired lock"); } CFastMutexGuard LOCK(s_ProcessLock); // Check that lock with specified name not already locked // in the current process. TLocks::iterator it = s_Locks->find(m_SystemName); _VERIFY(it != s_Locks->end()); if ( it->second > 1 ) { // Just decrease reference counter it->second--; return; } // Release lock #if defined(NCBI_OS_UNIX) # if defined(F_TLOCK) int res = lockf(m_Handle, F_ULOCK, 0); # elif defined(F_SETLK) struct flock lockparam; lockparam.l_type = F_UNLCK; lockparam.l_whence = SEEK_SET; lockparam.l_start = 0; lockparam.l_len = 0; /* whole file */ int res = fcntl(m_Handle, F_SETLK, &lockparam); # else # error "No supported lock method. Please port this code." # endif if ( res < 0 ) { NCBI_THROW(CInterProcessLockException, eUnlockError, "Cannot release the lock"); } close(m_Handle); #elif defined(NCBI_OS_MSWIN) if ( !::ReleaseMutex(m_Handle) ) { NCBI_THROW(CInterProcessLockException, eUnlockError, "Cannot release the lock"); } ::CloseHandle(m_Handle); #endif m_Handle = kInvalidLockHandle; s_Locks->erase(m_SystemName); }
void CInterProcessLock::Lock(const CTimeout& timeout, const CTimeout& granularity) { CFastMutexGuard LOCK(s_ProcessLock); // Check that lock with specified name not already locked // in the current process. TLocks::iterator it = s_Locks->find(m_SystemName); if (m_Handle != kInvalidLockHandle) { // The lock is already set in this CInterProcessLock object, // just increase reference counter. _VERIFY(it != s_Locks->end()); it->second++; return; } else { if (it != s_Locks->end()) { // The lock already exists in the current process. // We can use one CInterProcessLock object with // multiple Lock() calls, but not with different // CInterProcessLock objects. For example, on MS-Windows, // we cannot wait on the same mutex in the same thread. // So, two different objects can set locks simultaneously. // And for OS-compatibility we can do nothing here, // except throwing an exception. NCBI_THROW(CInterProcessLockException, eMultipleLocks, "Attempt to lock already locked object " \ "in the same process"); } } // Try to acquire a lock with specified timeout #if defined(NCBI_OS_UNIX) // Open lock file mode_t perm = CDirEntry::MakeModeT( CDirEntry::fRead | CDirEntry::fWrite /* user */, CDirEntry::fRead | CDirEntry::fWrite /* group */, 0, 0 /* other & special */); int fd = open(m_SystemName.c_str(), O_CREAT | O_RDWR, perm); if (fd == -1) { NCBI_THROW(CInterProcessLockException, eCreateError, string("Error creating lockfile ") + m_SystemName + ": " + strerror(errno)); } // Try to acquire the lock int x_errno = 0; if (timeout.IsInfinite() || timeout.IsDefault()) { while ((x_errno = s_UnixLock(fd))) { if (errno != EAGAIN) break; } } else { unsigned long ms = timeout.GetAsMilliSeconds(); if ( !ms ) { // Timeout == 0 x_errno = s_UnixLock(fd); } else { // Timeout > 0 unsigned long ms_gran; if ( granularity.IsInfinite() || granularity.IsDefault() ) { ms_gran = min(ms/5, (unsigned long)500); } else { ms_gran = granularity.GetAsMilliSeconds(); } // Try to lock within specified timeout for (;;) { x_errno = s_UnixLock(fd); if ( !x_errno ) { // Successfully locked break; } if (x_errno != EACCES && x_errno != EAGAIN ) { // Error break; } // Otherwise -- sleep granularity timeout unsigned long ms_sleep = ms_gran; if (ms_sleep > ms) { ms_sleep = ms; } if ( !ms_sleep ) { break; } SleepMilliSec(ms_sleep); ms -= ms_sleep; } // Timeout if ( !ms ) { close(fd); NCBI_THROW(CInterProcessLockException, eLockTimeout, "The lock could not be acquired in the time " \ "allotted"); } } // if (!ms) } // if (timeout.IsInfinite()) // Error if ( x_errno ) { close(fd); NCBI_THROW(CInterProcessLockException, eLockError, "Error creating lock"); } // Success m_Handle = fd; #elif defined(NCBI_OS_MSWIN) HANDLE handle = ::CreateMutex(NULL, TRUE, _T_XCSTRING(m_SystemName)); errno_t errcode = ::GetLastError(); if (handle == kInvalidLockHandle) { switch(errcode) { case ERROR_ACCESS_DENIED: // Mutex with specified name already exists, // but we don't have enough rights to open it. NCBI_THROW(CInterProcessLockException, eLockError, "The lock already exists"); break; case ERROR_INVALID_HANDLE: // Some system object with the same name already exists NCBI_THROW(CInterProcessLockException, eLockError, "Error creating lock, system object with the same" \ "name already exists"); break; default: // Unknown error NCBI_THROW(CInterProcessLockException, eCreateError, "Error creating lock"); break; } } else { // Mutex with specified name already exists if (errcode == ERROR_ALREADY_EXISTS) { // Wait DWORD res; if (timeout.IsInfinite() || timeout.IsDefault()) { res = WaitForSingleObject(handle, INFINITE); } else { res = WaitForSingleObject(handle, timeout.GetAsMilliSeconds()); } switch(res) { case WAIT_OBJECT_0: // The lock has been acquired break; case WAIT_TIMEOUT: ::CloseHandle(handle); NCBI_THROW(CInterProcessLockException, eLockTimeout, "The lock could not be acquired in the time " \ "allotted"); break; case WAIT_ABANDONED: // The lock is in abandoned state... Other thread/process // owning it was terminated. We can reuse this mutex, but // it is better to wait until it will be released by OS. /* FALLTHRU */ default: ::CloseHandle(handle); NCBI_THROW(CInterProcessLockException, eLockError, "Error creating lock"); break; } } m_Handle = handle; } #endif // Set reference counter to 1 (*s_Locks)[m_SystemName] = 1; }