summaryrefslogtreecommitdiff
path: root/lib/common/NamedLock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common/NamedLock.cpp')
-rw-r--r--lib/common/NamedLock.cpp223
1 files changed, 176 insertions, 47 deletions
diff --git a/lib/common/NamedLock.cpp b/lib/common/NamedLock.cpp
index f96f80b5..8e672ff5 100644
--- a/lib/common/NamedLock.cpp
+++ b/lib/common/NamedLock.cpp
@@ -21,8 +21,9 @@
#include <sys/file.h>
#endif
-#include "NamedLock.h"
#include "CommonException.h"
+#include "NamedLock.h"
+#include "Utils.h"
#include "MemLeakFindOn.h"
@@ -35,7 +36,11 @@
//
// --------------------------------------------------------------------------
NamedLock::NamedLock()
- : mFileDescriptor(-1)
+#ifdef WIN32
+: mFileDescriptor(INVALID_HANDLE_VALUE)
+#else
+: mFileDescriptor(-1)
+#endif
{
}
@@ -49,7 +54,11 @@ NamedLock::NamedLock()
// --------------------------------------------------------------------------
NamedLock::~NamedLock()
{
+#ifdef WIN32
+ if(mFileDescriptor != INVALID_HANDLE_VALUE)
+#else
if(mFileDescriptor != -1)
+#endif
{
ReleaseLock();
}
@@ -68,76 +77,151 @@ NamedLock::~NamedLock()
bool NamedLock::TryAndGetLock(const std::string& rFilename, int mode)
{
// Check
+#ifdef WIN32
+ if(mFileDescriptor != INVALID_HANDLE_VALUE)
+#else
if(mFileDescriptor != -1)
+#endif
{
THROW_EXCEPTION(CommonException, NamedLockAlreadyLockingSomething)
}
+ mFileName = rFilename;
+
// See if the lock can be got
+ int flags = O_WRONLY | O_CREAT | O_TRUNC;
+
#if HAVE_DECL_O_EXLOCK
- int fd = ::open(rFilename.c_str(),
- O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC | O_EXLOCK, mode);
- if(fd != -1)
- {
- // Got a lock, lovely
- mFileDescriptor = fd;
- return true;
- }
-
- // Failed. Why?
- if(errno != EWOULDBLOCK)
- {
- // Not the expected error
- THROW_EXCEPTION(CommonException, OSFileError)
- }
+ flags |= O_NONBLOCK | O_EXLOCK;
+ BOX_TRACE("Trying to create lockfile " << rFilename << " using O_EXLOCK");
+#elif defined BOX_OPEN_LOCK
+ flags |= BOX_OPEN_LOCK;
+ BOX_TRACE("Trying to create lockfile " << rFilename << " using BOX_OPEN_LOCK");
+#elif !HAVE_DECL_F_SETLK && !defined HAVE_FLOCK
+ // We have no other way to get a lock, so all we can do is fail if
+ // the file already exists, and take the risk of stale locks.
+ flags |= O_EXCL;
+ BOX_TRACE("Trying to create lockfile " << rFilename << " using O_EXCL");
+#else
+ BOX_TRACE("Trying to create lockfile " << rFilename << " without special flags");
+#endif
- return false;
+#ifdef WIN32
+ HANDLE fd = openfile(rFilename.c_str(), flags, mode);
+ if(fd == INVALID_HANDLE_VALUE)
#else
- int fd = ::open(rFilename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
+ int fd = ::open(rFilename.c_str(), flags, mode);
if(fd == -1)
- {
- BOX_WARNING("Failed to open lockfile: " << rFilename);
- THROW_EXCEPTION(CommonException, OSFileError)
- }
-
-#ifdef HAVE_FLOCK
- if(::flock(fd, LOCK_EX | LOCK_NB) != 0)
- {
- ::close(fd);
+#endif
+#if HAVE_DECL_O_EXLOCK
+ { // if()
if(errno == EWOULDBLOCK)
{
+ // Lockfile already exists, and we tried to open it
+ // exclusively, which means we failed to lock it.
+ BOX_NOTICE("Failed to lock lockfile with O_EXLOCK: " << rFilename
+ << ": already locked by another process?");
return false;
}
else
{
- THROW_EXCEPTION(CommonException, OSFileError)
+ THROW_SYS_FILE_ERROR("Failed to open lockfile with O_EXLOCK",
+ rFilename, CommonException, OSFileError);
}
}
-#elif HAVE_DECL_F_SETLK
- struct flock desc;
- desc.l_type = F_WRLCK;
- desc.l_whence = SEEK_SET;
- desc.l_start = 0;
- desc.l_len = 0;
- if(::fcntl(fd, F_SETLK, &desc) != 0)
- {
- ::close(fd);
- if(errno == EAGAIN)
+#else // !HAVE_DECL_O_EXLOCK
+ { // if()
+# if defined BOX_OPEN_LOCK
+ if(errno == EBUSY)
+# else // !BOX_OPEN_LOCK
+ if(errno == EEXIST && (flags & O_EXCL))
+# endif
{
+ // Lockfile already exists, and we tried to open it
+ // exclusively, which means we failed to lock it.
+ BOX_NOTICE("Failed to lock lockfile with O_EXCL: " << rFilename
+ << ": already locked by another process?");
return false;
}
else
{
- THROW_EXCEPTION(CommonException, OSFileError)
+ THROW_SYS_FILE_ERROR("Failed to open lockfile with O_EXCL",
+ rFilename, CommonException, OSFileError);
}
}
-#endif
+
+ try
+ {
+# ifdef HAVE_FLOCK
+ BOX_TRACE("Trying to lock lockfile " << rFilename << " using flock()");
+ if(::flock(fd, LOCK_EX | LOCK_NB) != 0)
+ {
+ if(errno == EWOULDBLOCK)
+ {
+ ::close(fd);
+ BOX_NOTICE("Failed to lock lockfile with flock(): " << rFilename
+ << ": already locked by another process");
+ return false;
+ }
+ else
+ {
+ THROW_SYS_FILE_ERROR("Failed to lock lockfile with flock()",
+ rFilename, CommonException, OSFileError);
+ }
+ }
+# elif HAVE_DECL_F_SETLK
+ struct flock desc;
+ desc.l_type = F_WRLCK;
+ desc.l_whence = SEEK_SET;
+ desc.l_start = 0;
+ desc.l_len = 0;
+ BOX_TRACE("Trying to lock lockfile " << rFilename << " using fcntl()");
+ if(::fcntl(fd, F_SETLK, &desc) != 0)
+ {
+ if(errno == EAGAIN)
+ {
+ ::close(fd);
+ BOX_NOTICE("Failed to lock lockfile with fcntl(): " << rFilename
+ << ": already locked by another process");
+ return false;
+ }
+ else
+ {
+ THROW_SYS_FILE_ERROR("Failed to lock lockfile with fcntl()",
+ rFilename, CommonException, OSFileError);
+ }
+ }
+# endif
+ }
+ catch(BoxException &e)
+ {
+# ifdef WIN32
+ CloseHandle(fd);
+# else
+ ::close(fd);
+# endif
+ BOX_NOTICE("Failed to lock lockfile " << rFilename << ": " << e.what());
+ throw;
+ }
+#endif // HAVE_DECL_O_EXLOCK
+
+ if(!FileExists(rFilename))
+ {
+ BOX_ERROR("Locked lockfile " << rFilename << ", but lockfile no longer "
+ "exists, bailing out");
+# ifdef WIN32
+ CloseHandle(fd);
+# else
+ ::close(fd);
+# endif
+ return false;
+ }
// Success
mFileDescriptor = fd;
+ BOX_TRACE("Successfully locked lockfile " << rFilename);
return true;
-#endif
}
// --------------------------------------------------------------------------
@@ -151,20 +235,65 @@ bool NamedLock::TryAndGetLock(const std::string& rFilename, int mode)
void NamedLock::ReleaseLock()
{
// Got a lock?
+#ifdef WIN32
+ if(mFileDescriptor == INVALID_HANDLE_VALUE)
+#else
if(mFileDescriptor == -1)
+#endif
{
THROW_EXCEPTION(CommonException, NamedLockNotHeld)
}
-
+
+#ifndef WIN32
+ // Delete the file. We need to do this before closing the filehandle,
+ // if we used flock() or fcntl() to lock it, otherwise someone could
+ // acquire the lock, release and delete it between us closing (and
+ // hence releasing) and deleting it, and we'd fail when it came to
+ // deleting the file. This happens in tests much more often than
+ // you'd expect!
+ //
+ // This doesn't apply on systems using plain lockfile locking, such as
+ // Windows, and there we need to close the file before deleting it,
+ // otherwise the system won't let us delete it.
+
+ if(::unlink(mFileName.c_str()) != 0)
+ {
+ THROW_EMU_ERROR(
+ BOX_FILE_MESSAGE(mFileName, "Failed to delete lockfile"),
+ CommonException, OSFileError);
+ }
+#endif // !WIN32
+
// Close the file
+# ifdef WIN32
+ if(!CloseHandle(mFileDescriptor))
+# else
if(::close(mFileDescriptor) != 0)
+# endif
{
- THROW_EXCEPTION(CommonException, OSFileError)
+ THROW_EMU_ERROR(
+ BOX_FILE_MESSAGE(mFileName, "Failed to close lockfile"),
+ CommonException, OSFileError);
}
- // Mark as unlocked
- mFileDescriptor = -1;
-}
+ // Mark as unlocked, so we don't try to close it again if the unlink() fails.
+#ifdef WIN32
+ mFileDescriptor = INVALID_HANDLE_VALUE;
+#else
+ mFileDescriptor = -1;
+#endif
+#ifdef WIN32
+ // On Windows we need to close the file before deleting it, otherwise
+ // the system won't let us delete it.
+ if(::unlink(mFileName.c_str()) != 0)
+ {
+ THROW_EMU_ERROR(
+ BOX_FILE_MESSAGE(mFileName, "Failed to delete lockfile"),
+ CommonException, OSFileError);
+ }
+#endif // WIN32
+ BOX_TRACE("Released lock and deleted lockfile " << mFileName);
+}