1554 lines
61 KiB
C++
1554 lines
61 KiB
C++
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved.
|
|
// Copyright (c) 2016 Sequence Logic, Inc. All rights reserved.
|
|
//
|
|
// File locking functions and classes
|
|
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#ifdef WIN32
|
|
#include <io.h>
|
|
#include <Windows.h>
|
|
#include <psapi.h>
|
|
#ifdef WIN64
|
|
typedef __int64 ssize_t;
|
|
#else
|
|
typedef __int32 ssize_t;
|
|
#endif
|
|
#else
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "eyeconstants.h"
|
|
#include "eyejson.h"
|
|
#include "eyelock.h"
|
|
#include "eyelog.h"
|
|
#include "eyetime.h"
|
|
#include "eyeutils.h"
|
|
|
|
using namespace std;
|
|
using namespace sequencelogic;
|
|
|
|
namespace sequencelogic {
|
|
pthread_mutex_t ionu_filelock_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
#ifdef LOCK_STRATEGY_MEMORY
|
|
EyeFileLocker* EyeFileLocker::_instance = nullptr;
|
|
std::mutex EyeFileLocker::_threadLocker;
|
|
#endif
|
|
static std::string LastEyeLockMessage = "";
|
|
#ifdef WIN32
|
|
TCHAR IONU_MASTER_LOCK_SHARED[] = TEXT ("Local\\EyeFileLockMappingObject");
|
|
HANDLE hMapFile = INVALID_HANDLE_VALUE;
|
|
LPTSTR pBuf = nullptr;
|
|
#endif
|
|
}
|
|
|
|
|
|
#define IONU_MASTER_LOCK_FILE ".IONU_eyefile_lock.lk"
|
|
#define IONU_FILE_LOCK_MAX_SIZE 16384
|
|
#define IONU_FILE_LOCK_ATTEMPTS 3 // Number of tries before returning timed out
|
|
#define IONU_FILE_LOCK_WAIT 1000 // Number of micro seconds for timeout interval
|
|
#define IONU_FILE_LOCK_WRITE_BIAS 3 // Number of write attempts vs. read
|
|
#define IONU_FILE_LOCK_EXPIRE 3600 // Number of seconds before lock expires
|
|
|
|
const std::string sequencelogic::GetLastEyeLockMessage()
|
|
{
|
|
return LastEyeLockMessage;
|
|
}
|
|
|
|
// Release a lock without respect to existing owners, called after shred and delete of .ionu
|
|
void sequencelogic::ReleaseEyeLock (const std::string filename)
|
|
{
|
|
#ifdef LOCK_STRATEGY_DOTLK
|
|
// Remove any old locks
|
|
std::string lockfile = sequencelogic::TempFilename (filename, ".lk");
|
|
#ifdef WIN32
|
|
DeleteFile (lockfile.c_str());
|
|
#else
|
|
remove (lockfile.c_str());
|
|
#endif
|
|
#elif defined (LOCK_STRATEGY_MASTER)
|
|
sequencelogic::LockFile (filename, "", IONU_FILE_LOCK_REMOVE);
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
// Helper functions to ensure all data is read/written
|
|
#ifdef WIN32
|
|
bool sequencelogic::ReadLockFile (HANDLE fd, const char* buf, size_t bytes)
|
|
{
|
|
DWORD numread;
|
|
if (ReadFile (fd, (char*)buf, (DWORD)bytes, &numread, NULL) && numread == bytes) {
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
#else
|
|
bool sequencelogic::ReadLockFile (int fd, const char* buf, size_t bytes)
|
|
{
|
|
size_t toread, nread = 0;
|
|
ssize_t result;
|
|
do {
|
|
toread = bytes - nread;
|
|
result = read (fd, (char*)buf + nread, toread);
|
|
if (result >= 0)
|
|
nread += result;
|
|
else if (errno != EINTR)
|
|
return false;
|
|
} while (nread < bytes);
|
|
return nread > 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
bool sequencelogic::WriteLockFile (HANDLE fd, const char* buf, size_t bytes)
|
|
{
|
|
if (SetFilePointer (fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
|
|
return false;
|
|
}
|
|
DWORD numwrite;
|
|
if (WriteFile (fd, (char*)buf, (DWORD)bytes, &numwrite, NULL) && numwrite == bytes) {
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
#else
|
|
bool sequencelogic::WriteLockFile (int fd, const char* buf, size_t bytes)
|
|
{
|
|
size_t towrite, nwrite = 0;
|
|
ssize_t result;
|
|
if (lseek (fd, 0, SEEK_SET) != 0)
|
|
return false;
|
|
do {
|
|
towrite = bytes - nwrite;
|
|
result = write (fd, (char*)buf + nwrite, towrite);
|
|
if (result >= 0)
|
|
nwrite += result;
|
|
else if (errno != EINTR)
|
|
return false;
|
|
} while (nwrite < bytes);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Create a new lock file
|
|
#ifdef WIN32
|
|
IONU_FILE_LOCK_STATUS sequencelogic::CreateLockFile (HANDLE fd, const std::string& lockfile, const std::string&strpid, const std::string& id, const std::string& rwlock, const std::string& time)
|
|
#else
|
|
IONU_FILE_LOCK_STATUS sequencelogic::CreateLockFile (int fd, const std::string& lockfile, const std::string&strpid, const std::string& id, const std::string& rwlock, const std::string& time)
|
|
#endif
|
|
{
|
|
IONU_FILE_LOCK_STATUS rc = IONU_FILE_LOCK_OK;
|
|
bool removeit = false;
|
|
|
|
if (rwlock == "U")
|
|
removeit = true;
|
|
else {
|
|
EyeJSONObject* locks = new EyeJSONObject (NULL, JSON_ARRAY, NULL);
|
|
if (locks) {
|
|
EyeJSONObject* lock = new EyeJSONObject (NULL, JSON_OBJECT, locks);
|
|
locks->AddMember (lock);
|
|
if (lock) {
|
|
lock->AddMember (new EyeJSONScalar ("pid", strpid.c_str(), JSON_STRING));
|
|
lock->AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
lock->AddMember (new EyeJSONScalar ("type", rwlock.c_str(), JSON_STRING));
|
|
lock->AddMember (new EyeJSONScalar ("time", time.c_str(), JSON_STRING));
|
|
}
|
|
std::string json;
|
|
locks->GetJSONString (json);
|
|
//cout << "creating lock file: " << json << std::endl;
|
|
delete locks;
|
|
if (json.length() > 24) {
|
|
// Always include the null termination as we are overwriting
|
|
if (!WriteLockFile (fd, json.c_str(), json.length() + 1)) {
|
|
IONUERROR ("sequencelogic::LockFile() - unable to write lock file: %d", errno);
|
|
rc = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
rc = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
#ifdef WIN32
|
|
CloseHandle (fd);
|
|
#else
|
|
close (fd);
|
|
#endif
|
|
if (removeit)
|
|
sequencelogic::RemoveFile (lockfile);
|
|
return rc;
|
|
}
|
|
|
|
// Update an existing lock file
|
|
#ifdef WIN32
|
|
IONU_FILE_LOCK_STATUS sequencelogic::UpdateLockFile (HANDLE fd, std::string& lockfile, EyeJSONObject* locks)
|
|
#else
|
|
IONU_FILE_LOCK_STATUS sequencelogic::UpdateLockFile (int fd, std::string& lockfile, EyeJSONObject* locks)
|
|
#endif
|
|
{
|
|
IONU_FILE_LOCK_STATUS rc = IONU_FILE_LOCK_OK;
|
|
std::string json;
|
|
bool removeit = false;
|
|
|
|
//cout << "Updating lock file: " << json << std::endl;
|
|
// No more locks so remove the lock file
|
|
if (!locks || locks->GetNumMembers() == 0) {
|
|
WriteLockFile (fd, "", 1);
|
|
//cout << "sequencelogic::LockFile(" << lockfile << ") - removed lock file" << std::endl;
|
|
removeit = true;
|
|
}
|
|
else {
|
|
locks->GetJSONString (json);
|
|
// Always include the null termination as we are overwriting
|
|
if (!WriteLockFile (fd, json.c_str(), json.length() + 1)) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to write lock file: %d", lockfile.c_str(), errno);
|
|
rc = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
#ifdef WIN32
|
|
CloseHandle (fd);
|
|
#else
|
|
close (fd);
|
|
#endif
|
|
if (removeit)
|
|
sequencelogic::RemoveFile (lockfile);
|
|
return rc;
|
|
}
|
|
|
|
bool sequencelogic::CheckActiveProcess (const std::string pid)
|
|
{
|
|
bool active = true;
|
|
stringstream ss;
|
|
ss << pid;
|
|
#ifdef WIN32
|
|
DWORD lpid = 0;
|
|
ss >> lpid;
|
|
// Get the list of process identifiers.
|
|
|
|
DWORD aProcesses[1024], cbNeeded, cProcesses;
|
|
|
|
if (!EnumProcesses (aProcesses, sizeof(aProcesses), &cbNeeded)) {
|
|
// Failed, try the OpenProcess option, which won't work on privileged processes
|
|
HANDLE processHandle = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, lpid);
|
|
if (processHandle == 0) { // Process is no longer running, delete all of it's locks
|
|
active = false;
|
|
}
|
|
else
|
|
CloseHandle(processHandle);
|
|
}
|
|
else {
|
|
// Calculate how many process identifiers were returned.
|
|
cProcesses = cbNeeded / sizeof(DWORD);
|
|
|
|
active = false;
|
|
for (unsigned int i = 0; i < cProcesses; ++i) {
|
|
if (aProcesses[i] == lpid) {
|
|
active = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
pid_t lpid = 0;
|
|
ss >> lpid;
|
|
if (kill (lpid, 0) == -1) {
|
|
if (errno != ESRCH) {
|
|
IONUDEBUG ("sequencelogic::LockFile() - unable to find lock owner: %d", errno);
|
|
}
|
|
active = false;
|
|
}
|
|
#endif
|
|
return active;
|
|
}
|
|
|
|
void sequencelogic::CleanLocks (EyeJSONObject* locks, const std::string& strpid, const std::string& id, IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
if (!locks)
|
|
return;
|
|
|
|
EyeTime lktime;
|
|
|
|
// Remove all stale locks
|
|
for (int index = (int)locks->GetNumMembers() - 1; index >= 0; index--) {
|
|
EyeJSONObject* lock = static_cast<EyeJSONObject*>(locks->GetMember (index));
|
|
if (lock) {
|
|
EyeJSONScalar* lockpid = static_cast<EyeJSONScalar*>(lock->GetMember("pid"));
|
|
EyeJSONScalar* lockid = static_cast<EyeJSONScalar*>(lock->GetMember("id"));
|
|
EyeJSONScalar* locktype = static_cast<EyeJSONScalar*>(lock->GetMember("type"));
|
|
EyeJSONScalar* locktime = static_cast<EyeJSONScalar*>(lock->GetMember("time"));
|
|
if (!lockpid || !lockid || !locktype || !locktime) {
|
|
return;
|
|
}
|
|
lktime.SetTime (locktime->GetValue());
|
|
|
|
// Check if the lock has expired
|
|
if (lktime.SecondsFromNow() > IONU_FILE_LOCK_EXPIRE) {
|
|
IONUDEBUG ("sequencelogic::LockFile() - removing stale lock (%f') for pid: %s", lktime.SecondsFromNow(), lockpid->GetValue());
|
|
locks->RemoveMember (index);
|
|
continue;
|
|
}
|
|
// Does not belong to this process, if hosts match check if process is running
|
|
else if (strpid.compare (lockpid->GetValue()) != 0) {
|
|
std::vector<std::string> lockedPID = sequencelogic::SplitString (lockpid->GetValue(), ':');
|
|
std::vector<std::string> currentPID = sequencelogic::SplitString (strpid, ':');
|
|
if (lockedPID.size() != 2) {
|
|
IONUDEBUG ("sequencelogic::LockFile() - removing invalid lock for pid: %s", lockpid->GetValue());
|
|
locks->RemoveMember (index);
|
|
}
|
|
// Check if locked on this host and if the process is still running
|
|
else if (lockedPID[0] == currentPID[0] && !CheckActiveProcess (lockedPID[1])) {
|
|
locks->RemoveMember (index);
|
|
IONUDEBUG ("sequencelogic::LockFile() - removed old lock: %s", lockpid->GetValue());
|
|
}
|
|
|
|
}
|
|
// Never block on a clear
|
|
else if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
if (strpid.compare (lockpid->GetValue()) == 0 && id.compare (lockid->GetValue()) == 0) {
|
|
//IONUDEBUG ("sequencelogic::LockFile() - popping lock %s for %s:%s", locktype->GetValue(), lockpid->GetValue(), lockid->GetValue());
|
|
char* val = locktype->GetValue();
|
|
size_t len = sequencelogic::StrLen (val, 64);
|
|
if (len > 1) {
|
|
val[len-1] = '\0';
|
|
locktype->ReplaceValue (val);
|
|
}
|
|
else {
|
|
locks->RemoveMember (index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef LOCK_STRATEGY_DOTLK
|
|
|
|
IONU_FILE_LOCK_STATUS sequencelogic::LockFile (const std::string& filename, const std::string& id, IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
if (!sequencelogic::IsValidFilename (filename) || id.size() == 0) {
|
|
IONUERROR ("sequencelogic::LockFile() - invalid filename or id");
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
if (sequencelogic::IsLockFile (filename)) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - can't lock a lockfile", filename.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
|
|
IONU_FILE_LOCK_STATUS rc = IONU_FILE_LOCK_ERROR;
|
|
|
|
const char* rwlock = (ltype == IONU_FILE_LOCK_READ ? "R" : (ltype == IONU_FILE_LOCK_WRITE ? "W" : "U"));
|
|
std::string lockfile = sequencelogic::TempFilename (filename, ".lk");
|
|
|
|
#ifdef WIN32
|
|
HANDLE fd;
|
|
#else
|
|
int fd;
|
|
#endif
|
|
int attempt;
|
|
bool result = false;
|
|
bool waiting = false;
|
|
std::string json;
|
|
|
|
// Get the host:pid identifier
|
|
char hostname[32];
|
|
#ifdef WIN32
|
|
DWORD pid = GetCurrentProcessId();
|
|
#else
|
|
pid_t pid = getpid();
|
|
#endif
|
|
stringstream sspid;
|
|
if (gethostname (hostname, 31) == 0)
|
|
sspid << hostname << ":" << pid;
|
|
else
|
|
sspid << pid;
|
|
std::string strpid = sspid.str();
|
|
|
|
EyeTime now;
|
|
|
|
EyeJSONObject* locks = nullptr;
|
|
EyeJSONObject* lock = nullptr;
|
|
char* buffer = nullptr;
|
|
|
|
//IONUDEBUG ("lock %s:%s:%s", strpid, id, rwlock);
|
|
// Set mutex lock while checking file locks to keep it all thread safe
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
|
|
for (attempt = 0; attempt < IONU_FILE_LOCK_ATTEMPTS; ++attempt) {
|
|
#ifdef WIN32
|
|
bool fexists = false;
|
|
if (_access (lockfile.c_str(), 0) == 0) // File exists
|
|
fexists = true;
|
|
if ((fd = CreateFile (lockfile.c_str(), (GENERIC_READ | GENERIC_WRITE), FILE_SHARE_READ, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL)) == INVALID_HANDLE_VALUE) {
|
|
if (::GetLastError() == ERROR_ACCESS_DENIED && ltype != IONU_FILE_LOCK_WRITE) {
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
if (fexists) {
|
|
// Access error, file exists, trying to unlock, sleep and retry
|
|
if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
std::this_thread::sleep_for(std::chrono::microseconds(IONU_FILE_LOCK_WAIT));
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
waiting = true;
|
|
continue;
|
|
}
|
|
// Read lock in readonly directory
|
|
else if (ltype == IONU_FILE_LOCK_READ)
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
}
|
|
else {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to open or create lock file: %s", filename.c_str(), sequencelogic::GetLastErrorMessage().c_str());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
if (fexists == true) {
|
|
DWORD wfsize = GetFileSize (fd, NULL);
|
|
if (wfsize == INVALID_FILE_SIZE) {
|
|
CloseHandle (fd);
|
|
IONUERROR ("sequencelogic::LockFile(%s) - GetFileSize failed on lock file: %d", filename.c_str(), errno);
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
size_t fsize = (size_t)wfsize;
|
|
#else
|
|
// Test for existance and create if not
|
|
if ((fd = open (lockfile.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRWXU)) == -1) {
|
|
// Read only directory and not requesting a write lock is success
|
|
if (errno == EACCES && ltype != IONU_FILE_LOCK_WRITE) {
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return (access (lockfile.c_str(), 0) == 0) ? IONU_FILE_LOCK_ERROR : IONU_FILE_LOCK_OK;
|
|
}
|
|
else if (errno != EEXIST) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to create lock file: %d", filename.c_str(), errno);
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
// File exists, open it
|
|
if ((fd = open (lockfile.c_str(), O_RDWR)) == -1) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to open lock file: %d", filename.c_str(), errno);
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
struct stat fileStat;
|
|
if (fstat (fd, &fileStat) < 0) {
|
|
close (fd);
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to stat lock file: %d", filename.c_str(), errno);
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
size_t fsize = (size_t)fileStat.st_size;
|
|
#endif
|
|
buffer = (char*) sequencelogic::New (fsize);
|
|
if (!buffer) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to allocate buffer", filename.c_str());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
result = ReadLockFile (fd, buffer, fsize);
|
|
if (result && fsize > 24) {
|
|
// Read the existing lock file into memory
|
|
//IONUDEBUG ("%s.lk - %s %s:%s", filename, buffer, strpid.c_str(), id.c_str());
|
|
if (locks) delete locks;
|
|
locks = new EyeJSONObject (buffer);
|
|
delete[] buffer;
|
|
buffer = nullptr;
|
|
if (!locks) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - unable to allocate locks object", filename.c_str());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
|
|
// Remove any stale locks, and process unlock request
|
|
CleanLocks (locks, strpid, id, ltype);
|
|
if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
rc = UpdateLockFile (fd, lockfile, locks);
|
|
delete locks;
|
|
locks = nullptr;
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return rc;
|
|
}
|
|
|
|
// Loop through the existing locks and process the requested lock
|
|
for (size_t index = 0; index < locks->GetNumMembers(); ++index) {
|
|
lock = static_cast<EyeJSONObject*>(locks->GetMember (index));
|
|
if (lock) {
|
|
EyeJSONScalar* lockpid = static_cast<EyeJSONScalar*>(lock->GetMember("pid"));
|
|
EyeJSONScalar* lockid = static_cast<EyeJSONScalar*>(lock->GetMember("id"));
|
|
EyeJSONScalar* locktype = static_cast<EyeJSONScalar*>(lock->GetMember("type"));
|
|
EyeJSONScalar* locktime = static_cast<EyeJSONScalar*>(lock->GetMember("time"));
|
|
if (!lockpid || !lockid || !locktype || !locktime) {
|
|
delete locks;
|
|
locks = nullptr;
|
|
#ifdef WIN32
|
|
CloseHandle (fd);
|
|
#else
|
|
close (fd);
|
|
#endif
|
|
sequencelogic::RemoveFile (lockfile);
|
|
IONUERROR ("sequencelogic::LockFile(%s) - invalid lock file", filename.c_str());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
// Check if the lock belongs to this process
|
|
else if (strpid.compare (lockpid->GetValue()) == 0) {
|
|
// Check if the lock belongs to this thread/id
|
|
if (id.compare (lockid->GetValue()) != 0) {
|
|
// Existing or requested W lock, wait and retry
|
|
if (ltype == IONU_FILE_LOCK_WRITE || strchr (locktype->GetValue(), 'W')) {
|
|
//IONUDEBUG ("sequencelogic::LockFile(%s) - thread %s waiting for %s lock on %s, attempt %d", filename, id.c_str(), rwlock, lockid->GetValue(), attempt);
|
|
#ifdef WIN32
|
|
CloseHandle (fd);
|
|
#else
|
|
close (fd);
|
|
#endif
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
if (ltype == IONU_FILE_LOCK_WRITE)
|
|
std::this_thread::sleep_for(std::chrono::microseconds(IONU_FILE_LOCK_WAIT / IONU_FILE_LOCK_WRITE_BIAS));
|
|
else
|
|
std::this_thread::sleep_for(std::chrono::microseconds(IONU_FILE_LOCK_WAIT));
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = (strcmp (locktype->GetValue(), "W") == 0) ? "Write lock at " : "Read lock at ";
|
|
LastEyeLockMessage += locktime->GetValue();
|
|
LastEyeLockMessage += " by: ";
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
waiting = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (ltype == IONU_FILE_LOCK_WRITE && locks->GetNumMembers() > 1) {
|
|
//IONUDEBUG ("sequencelogic::LockFile(%s) - thread %s waiting for %s lock on %s, attempt %d", filename, id.c_str(), rwlock, lockid->GetValue(), attempt);
|
|
#ifdef WIN32
|
|
CloseHandle (fd);
|
|
#else
|
|
close (fd);
|
|
#endif
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
std::this_thread::sleep_for(std::chrono::microseconds (IONU_FILE_LOCK_WAIT));
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = (strcmp (locktype->GetValue(), "W") == 0) ? "Write lock at " : "Read lock at ";
|
|
LastEyeLockMessage += locktime->GetValue();
|
|
LastEyeLockMessage += " by: ";
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
waiting = true;
|
|
break;
|
|
}
|
|
else {
|
|
locktype->ReplaceValue (rwlock);
|
|
locktime->ReplaceValue (now.GetISO8601Time());
|
|
rc = UpdateLockFile (fd, lockfile, locks);
|
|
delete locks;
|
|
locks = nullptr;
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
else if (ltype == IONU_FILE_LOCK_WRITE || strchr (locktype->GetValue(), 'W')) {
|
|
//IONUDEBUG ("sequencelogic::LockFile(%s) - process %s waiting for %s lock on %s, attempt %d", filename.c_str(), strpid.c_str(), rwlock, lockpid->GetValue(), attempt);
|
|
#ifdef WIN32
|
|
CloseHandle (fd);
|
|
#else
|
|
close (fd);
|
|
#endif
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
if (ltype == IONU_FILE_LOCK_WRITE)
|
|
std::this_thread::sleep_for(std::chrono::microseconds(IONU_FILE_LOCK_WAIT / IONU_FILE_LOCK_WRITE_BIAS));
|
|
else
|
|
std::this_thread::sleep_for(std::chrono::microseconds(IONU_FILE_LOCK_WAIT));
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = (strcmp (locktype->GetValue(), "W") == 0) ? "Write lock at " : "Read lock at ";
|
|
LastEyeLockMessage += locktime->GetValue();
|
|
LastEyeLockMessage += " by: ";
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
waiting = true;
|
|
break;
|
|
}
|
|
}
|
|
} // end for
|
|
|
|
// Need to add a new lock
|
|
if (locks && !waiting) {
|
|
lock = new EyeJSONObject (NULL, JSON_OBJECT, locks);
|
|
locks->AddMember (lock);
|
|
lock->AddMember (new EyeJSONScalar ("pid", strpid.c_str(), JSON_STRING));
|
|
lock->AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
lock->AddMember (new EyeJSONScalar ("type", rwlock, JSON_STRING));
|
|
lock->AddMember (new EyeJSONScalar ("time", now.GetISO8601Time(), JSON_STRING));
|
|
rc = UpdateLockFile (fd, lockfile, locks);
|
|
delete locks;
|
|
locks = nullptr;
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return rc;
|
|
}
|
|
else if (locks) {
|
|
delete locks;
|
|
locks = nullptr;
|
|
}
|
|
waiting = false;
|
|
continue;
|
|
}
|
|
else {
|
|
if (buffer) {
|
|
delete[] buffer;
|
|
buffer = nullptr;
|
|
}
|
|
rc = CreateLockFile (fd, lockfile, strpid, id, rwlock, now.GetISO8601Time());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return rc;
|
|
}
|
|
}
|
|
// Create new lock file, add requested lock
|
|
else {
|
|
rc = CreateLockFile (fd, lockfile, strpid, id, rwlock, now.GetISO8601Time());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if (attempt >= IONU_FILE_LOCK_ATTEMPTS) {
|
|
IONUDEBUG ("sequencelogic::LockFile(%s) - timeout trying to obtain %s lock for %s", filename.c_str(), rwlock, id.c_str());
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_TIMEOUT;
|
|
}
|
|
|
|
if (locks) {
|
|
delete locks;
|
|
locks = nullptr;
|
|
}
|
|
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
|
|
EyeFileLock::EyeFileLock(IONU_FILE_LOCK_TYPE ltype, const char* filename, const char* id)
|
|
:_id(id ? id : ""), _filename(filename), _ltype(ltype), _status(IONU_FILE_LOCK_ERROR)
|
|
{
|
|
if (_id.length() == 0) {
|
|
//Get a thread id, however your platform has to do that
|
|
#if defined(__MACH__)
|
|
mach_port_t tid = pthread_mach_thread_np(pthread_self());
|
|
#elif defined(WIN32)
|
|
int tid = ::GetCurrentThreadId();
|
|
#else
|
|
pthread_t tid = pthread_self();
|
|
#endif
|
|
|
|
std::stringstream tmpStr;
|
|
tmpStr << tid;
|
|
_id = tmpStr.str();
|
|
}
|
|
_status = LockFile(_filename.c_str(), _id.c_str(), _ltype);
|
|
}
|
|
|
|
EyeFileLock::~EyeFileLock()
|
|
{
|
|
LockFile(_filename.c_str(), _id.c_str(), IONU_FILE_LOCK_CLEAR);
|
|
}
|
|
|
|
bool EyeFileLock::convertToWrite()
|
|
{
|
|
//only state change from read to write allowed so we confirm read
|
|
if (_ltype != IONU_FILE_LOCK_READ) {
|
|
return false;
|
|
}
|
|
|
|
return acquire (IONU_FILE_LOCK_WRITE);
|
|
}
|
|
|
|
bool EyeFileLock::convertToRead()
|
|
{
|
|
// Only allow state change from write to read
|
|
if (_ltype != IONU_FILE_LOCK_WRITE) {
|
|
return false;
|
|
}
|
|
|
|
return acquire (IONU_FILE_LOCK_READ);
|
|
}
|
|
|
|
bool EyeFileLock::acquire (IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
_status = LockFile(_filename.c_str(), _id.c_str(), ltype);
|
|
if (_status == IONU_FILE_LOCK_OK) {
|
|
_ltype = ltype;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool EyeFileLock::release()
|
|
{
|
|
return acquire (IONU_FILE_LOCK_CLEAR);
|
|
}
|
|
|
|
sequencelogic::EyeFileReadLock::EyeFileReadLock(const char* filename, const char* id)
|
|
:EyeFileLock(IONU_FILE_LOCK_READ, filename, id)
|
|
{
|
|
}
|
|
|
|
sequencelogic::EyeFileWriteLock::EyeFileWriteLock(const char* filename, const char* id)
|
|
:EyeFileLock(IONU_FILE_LOCK_WRITE, filename, id)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
#ifdef LOCK_STRATEGY_MEMORY
|
|
EyeFileLock::EyeFileLock(IONU_FILE_LOCK_TYPE ltype, const std::string& filename, const std::string& id)
|
|
:_id(id), _filename(filename), _ltype(ltype), _status(IONU_FILE_LOCK_ERROR)
|
|
{
|
|
if (_id.length() == 0) {
|
|
//Get a thread id, however your platform has to do that
|
|
#if defined(__MACH__)
|
|
mach_port_t tid = pthread_mach_thread_np(pthread_self());
|
|
#elif defined(WIN32)
|
|
int tid = ::GetCurrentThreadId();
|
|
#else
|
|
pthread_t tid = pthread_self();
|
|
#endif
|
|
|
|
std::stringstream tmpStr;
|
|
tmpStr << tid;
|
|
_id = tmpStr.str();
|
|
}
|
|
_status = EyeFileLocker::GetInstance()->LockFile (_ltype, _filename, _id);
|
|
}
|
|
|
|
sequencelogic::EyeFileLock::~EyeFileLock()
|
|
{
|
|
EyeFileLocker::GetInstance()->LockFile (IONU_FILE_LOCK_CLEAR, _filename, _id);
|
|
}
|
|
|
|
bool EyeFileLock::convertToRead()
|
|
{
|
|
// Only allow state change from write to read
|
|
if (_ltype != IONU_FILE_LOCK_WRITE) {
|
|
return false;
|
|
}
|
|
|
|
return acquire (IONU_FILE_LOCK_READ);
|
|
}
|
|
|
|
bool EyeFileLock::convertToWrite()
|
|
{
|
|
// Only allow state change from read to write
|
|
if (_ltype != IONU_FILE_LOCK_READ) {
|
|
return false;
|
|
}
|
|
|
|
return acquire (IONU_FILE_LOCK_WRITE);
|
|
}
|
|
|
|
bool EyeFileLock::release()
|
|
{
|
|
return acquire (IONU_FILE_LOCK_CLEAR);
|
|
}
|
|
|
|
bool EyeFileLock::acquire (IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
_status = EyeFileLocker::GetInstance()->LockFile (ltype, _filename, _id);
|
|
if (_status == IONU_FILE_LOCK_OK) {
|
|
_ltype = ltype;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
EyeFileReadLock::EyeFileReadLock (const std::string& filename, const std::string& id)
|
|
:EyeFileLock (IONU_FILE_LOCK_READ, filename, id)
|
|
{
|
|
}
|
|
|
|
EyeFileWriteLock::EyeFileWriteLock (const std::string& filename, const std::string& id)
|
|
:EyeFileLock (IONU_FILE_LOCK_WRITE, filename, id)
|
|
{
|
|
}
|
|
|
|
IONU_FILE_LOCK_STATUS sequencelogic::LockFile (const std::string& filename, const std::string& id, IONU_FILE_LOCK_TYPE rwlock)
|
|
{
|
|
return EyeFileLocker::GetInstance()->LockFile (rwlock, filename, id);
|
|
}
|
|
|
|
EyeFileLocker::EyeFileLocker()
|
|
{
|
|
_instance = nullptr;
|
|
}
|
|
|
|
EyeFileLocker::~EyeFileLocker()
|
|
{
|
|
}
|
|
|
|
EyeFileLocker* EyeFileLocker::GetInstance ()
|
|
{
|
|
// Construct singleton if needed
|
|
if (_instance == nullptr) {
|
|
std::lock_guard<std::mutex> lock(_threadLocker);
|
|
// Check again in case instance was created while waiting for mutex
|
|
if (_instance == nullptr) {
|
|
_instance = new EyeFileLocker();
|
|
}
|
|
}
|
|
return _instance;
|
|
|
|
}
|
|
|
|
|
|
IONU_FILE_LOCK_STATUS EyeFileLocker::LockFile (IONU_FILE_LOCK_TYPE ltype, const std::string& filename, const std::string& id)
|
|
{
|
|
if (sequencelogic::IsLockFile (filename)) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - can't lock a lockfile", filename.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
|
|
// Try more attempts in the same time for write locks to give them a better chance
|
|
size_t retries = (ltype == IONU_FILE_LOCK_READ ? IONU_FILE_LOCK_ATTEMPTS : IONU_FILE_LOCK_ATTEMPTS * IONU_FILE_LOCK_WRITE_BIAS);
|
|
LastEyeLockMessage = "";
|
|
|
|
for (size_t attempt = 0; attempt < retries; ++attempt) {
|
|
switch (LockFileAttempt (filename, id, ltype)) {
|
|
case IONU_FILE_LOCK_OK:
|
|
return IONU_FILE_LOCK_OK;
|
|
break;
|
|
case IONU_FILE_LOCK_ERROR:
|
|
return IONU_FILE_LOCK_ERROR;
|
|
break;
|
|
case IONU_FILE_LOCK_TIMEOUT:
|
|
if (ltype == IONU_FILE_LOCK_WRITE)
|
|
std::this_thread::sleep_for(std::chrono::microseconds (IONU_FILE_LOCK_WAIT / IONU_FILE_LOCK_WRITE_BIAS));
|
|
else
|
|
std::this_thread::sleep_for(std::chrono::microseconds (IONU_FILE_LOCK_WAIT));
|
|
break;
|
|
default:
|
|
return IONU_FILE_LOCK_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
return IONU_FILE_LOCK_TIMEOUT;
|
|
}
|
|
|
|
// Attempt to acquire the requested lock, if valid but unable to obtain return IONU_FILE_LOCK_TIMEOUT for retry
|
|
IONU_FILE_LOCK_STATUS EyeFileLocker::LockFileAttempt (const std::string& filename, const std::string& id, IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
std::string rwlock = (ltype == IONU_FILE_LOCK_READ ? "R" : (ltype == IONU_FILE_LOCK_WRITE ? "W" : "U"));
|
|
std::lock_guard<std::mutex> lock(_threadLocker);
|
|
|
|
auto search = _lockmap.find (filename);
|
|
if (search != _lockmap.end ()) {
|
|
if (ltype == IONU_FILE_LOCK_REMOVE) {
|
|
_lockmap.erase (search);
|
|
//cout << "removed " << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
EyeJSONObject lock (search->second.c_str());
|
|
// Multiple locks on this file
|
|
if (lock.GetType() == JSON_ARRAY) {
|
|
if (ltype == IONU_FILE_LOCK_WRITE) {
|
|
//cout << "waiting on array " << endl;
|
|
EyeJSONObject* elock = static_cast<EyeJSONObject*>(lock.GetMember ((size_t)0));
|
|
EyeJSONScalar* lockid = static_cast<EyeJSONScalar*>(elock->GetMember("id"));
|
|
LastEyeLockMessage = "Read lock by: ";
|
|
LastEyeLockMessage += (lockid ? lockid->GetValue() : "unknown");
|
|
return IONU_FILE_LOCK_TIMEOUT;
|
|
}
|
|
else {
|
|
for (size_t i = 0; i < lock.GetNumMembers(); ++i) {
|
|
EyeJSONObject* elock = static_cast<EyeJSONObject*>(lock.GetMember (i));
|
|
EyeJSONScalar* lockid = static_cast<EyeJSONScalar*>(elock->GetMember("id"));
|
|
EyeJSONScalar* locktype = static_cast<EyeJSONScalar*>(elock->GetMember("type"));
|
|
if (lockid && locktype) {
|
|
if (id.compare (lockid->GetValue()) == 0) {
|
|
if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
lock.RemoveMember(i);
|
|
if (lock.GetNumMembers() > 1)
|
|
search->second = lock.Stringify();
|
|
else if (lock.GetNumMembers() == 1) {
|
|
elock = static_cast<EyeJSONObject*>(lock.GetMember ((size_t)0));
|
|
search->second = elock->Stringify();
|
|
}
|
|
//cout << "clear " << search->second << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
else if (ltype == IONU_FILE_LOCK_READ && strcmp (locktype->GetValue(), "R") == 0) {
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
else {
|
|
LastEyeLockMessage = (ltype == IONU_FILE_LOCK_READ ? "Write lock by: " : "Read lock by: ");
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add a new read lock
|
|
if (ltype == IONU_FILE_LOCK_READ) {
|
|
EyeJSONObject* alock = new EyeJSONObject (nullptr, JSON_OBJECT, nullptr);
|
|
alock->AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("type", rwlock.c_str(), JSON_STRING));
|
|
lock.AddMember (alock);
|
|
search->second = lock.Stringify();
|
|
//cout << "add " << search->second << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
else {
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
}
|
|
// Single lock on this file
|
|
else if (lock.GetType() == JSON_OBJECT) {
|
|
EyeJSONScalar* lockid = static_cast<EyeJSONScalar*>(lock.GetMember("id"));
|
|
EyeJSONScalar* locktype = static_cast<EyeJSONScalar*>(lock.GetMember("type"));
|
|
if (lockid && locktype) {
|
|
if (id.compare (lockid->GetValue()) == 0) {
|
|
if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
_lockmap.erase (search);
|
|
//cout << "cleared " << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
// Already have the requested lock type
|
|
else if (rwlock.compare (locktype->GetValue()) == 0) {
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
// Convert existing lock from read to write, or vice verse
|
|
else {
|
|
locktype->ReplaceValue (rwlock.c_str());
|
|
search->second = lock.Stringify();
|
|
//cout << "convert " << search->second << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
}
|
|
// Another thread has a read lock and requested lock is also a read lock, add both locks to array
|
|
else if (ltype == IONU_FILE_LOCK_READ && strcmp (locktype->GetValue(), "R") == 0) {
|
|
EyeJSONObject locks (nullptr, JSON_ARRAY, nullptr);
|
|
EyeJSONObject* elock = new EyeJSONObject (nullptr, JSON_OBJECT, nullptr);
|
|
elock->AddMember (new EyeJSONScalar ("id", lockid->GetValue(), JSON_STRING));
|
|
elock->AddMember (new EyeJSONScalar ("type", locktype->GetValue(), JSON_STRING));
|
|
locks.AddMember (elock);
|
|
elock = new EyeJSONObject (nullptr, JSON_OBJECT, nullptr);
|
|
elock->AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
elock->AddMember (new EyeJSONScalar ("type", rwlock.c_str(), JSON_STRING));
|
|
locks.AddMember (elock);
|
|
search->second = locks.Stringify();
|
|
//cout << "added " << search->second << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
// Clear request, but no lock by us
|
|
else if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
// Need to wait and retry
|
|
else {
|
|
//cout << "waiting on lock " << endl;
|
|
LastEyeLockMessage = (ltype == IONU_FILE_LOCK_READ ? "Write lock by: " : "Read lock by: ");
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
return IONU_FILE_LOCK_TIMEOUT;
|
|
}
|
|
}
|
|
else {
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
else if (ltype != IONU_FILE_LOCK_CLEAR) {
|
|
EyeJSONObject lock (nullptr, JSON_OBJECT, nullptr);
|
|
lock.AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
lock.AddMember (new EyeJSONScalar ("type", rwlock.c_str(), JSON_STRING));
|
|
_lockmap.insert (std::pair<std::string,std::string>(filename, lock.Stringify()));
|
|
//cout << "first " << lock.Stringify() << endl;
|
|
return IONU_FILE_LOCK_OK;
|
|
}
|
|
|
|
return ltype == IONU_FILE_LOCK_CLEAR ? IONU_FILE_LOCK_OK : IONU_FILE_LOCK_ERROR;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef LOCK_STRATEGY_MASTER
|
|
IONU_FILE_LOCK_STATUS sequencelogic::LockFile (const std::string& filename, const std::string& id, IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
if (sequencelogic::IsLockFile (filename)) {
|
|
IONUERROR ("sequencelogic::LockFile(%s) - can't lock a lockfile", filename.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
|
|
// Deal with case, separator, 8.3, relative, subst, network drive, long names, UNC...
|
|
std::string path = sequencelogic::Canonicalise (filename);
|
|
|
|
// Try more attempts in the same time for write locks to give them a better chance
|
|
size_t retries = (ltype == IONU_FILE_LOCK_READ ? IONU_FILE_LOCK_ATTEMPTS : IONU_FILE_LOCK_ATTEMPTS * IONU_FILE_LOCK_WRITE_BIAS);
|
|
|
|
for (size_t attempt = 0; attempt < retries; ++attempt) {
|
|
LastEyeLockMessage = "";
|
|
switch (LockFileAttempt (path, id, ltype)) {
|
|
case IONU_FILE_LOCK_OK:
|
|
return IONU_FILE_LOCK_OK;
|
|
break;
|
|
case IONU_FILE_LOCK_ERROR:
|
|
return IONU_FILE_LOCK_ERROR;
|
|
break;
|
|
case IONU_FILE_LOCK_TIMEOUT:
|
|
if (ltype == IONU_FILE_LOCK_WRITE)
|
|
std::this_thread::sleep_for(std::chrono::microseconds (IONU_FILE_LOCK_WAIT / IONU_FILE_LOCK_WRITE_BIAS));
|
|
else
|
|
std::this_thread::sleep_for(std::chrono::microseconds (IONU_FILE_LOCK_WAIT));
|
|
break;
|
|
default:
|
|
return IONU_FILE_LOCK_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
return IONU_FILE_LOCK_TIMEOUT;
|
|
}
|
|
|
|
EyeFileLock::EyeFileLock(IONU_FILE_LOCK_TYPE ltype, const std::string& filename, const std::string& id)
|
|
:_id(id), _filename(sequencelogic::Canonicalise(filename)), _ltype(ltype), _status(IONU_FILE_LOCK_ERROR)
|
|
{
|
|
if (_id.length() == 0) {
|
|
//Get a thread id, however your platform has to do that
|
|
#if defined(__MACH__)
|
|
mach_port_t tid = pthread_mach_thread_np(pthread_self());
|
|
#elif defined(WIN32)
|
|
int tid = ::GetCurrentThreadId();
|
|
#else
|
|
pthread_t tid = pthread_self();
|
|
#endif
|
|
|
|
std::stringstream tmpStr;
|
|
tmpStr << tid;
|
|
_id = tmpStr.str();
|
|
}
|
|
acquire (ltype);
|
|
}
|
|
|
|
EyeFileLock::~EyeFileLock()
|
|
{
|
|
bool released = release();
|
|
// We *really* don't want to fail, so try for a while if lock is not released
|
|
for (int i = 1; !released && i <= IONU_FILE_LOCK_ATTEMPTS; ++i) {
|
|
std::this_thread::sleep_for(std::chrono::microseconds(i * IONU_FILE_LOCK_WAIT));
|
|
released = release();
|
|
}
|
|
// TODO: If result is false, the lock is now orphaned
|
|
// This is really bad! Consider recording the orphaned lock and keep
|
|
// trying to release it?
|
|
if (!released) {
|
|
IONUDEBUG("EyeFileLock::~EyeFileLock() Failed to release lock on %s", _filename.c_str());
|
|
}
|
|
}
|
|
|
|
bool EyeFileLock::convertToRead()
|
|
{
|
|
// Only allow state change from write to read
|
|
if (_ltype != IONU_FILE_LOCK_WRITE) {
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = "No write lock by: ";
|
|
LastEyeLockMessage += _id.c_str();
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
IONUDEBUG ("EyeFileLock::convertToRead() %s", LastEyeLockMessage.c_str());
|
|
return false;
|
|
}
|
|
|
|
return acquire (IONU_FILE_LOCK_READ);
|
|
}
|
|
|
|
bool EyeFileLock::convertToWrite()
|
|
{
|
|
// Only allow state change from read to write
|
|
if (_ltype != IONU_FILE_LOCK_READ) {
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = "No read lock by: ";
|
|
LastEyeLockMessage += _id.c_str();
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
IONUDEBUG ("EyeFileLock::convertToRead() %s", LastEyeLockMessage.c_str());
|
|
return false;
|
|
}
|
|
|
|
return acquire (IONU_FILE_LOCK_WRITE);
|
|
}
|
|
|
|
bool EyeFileLock::release()
|
|
{
|
|
return acquire (IONU_FILE_LOCK_CLEAR);
|
|
}
|
|
|
|
bool EyeFileLock::acquire(IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
_status = LockFile (_filename, _id, ltype);
|
|
if (_status == IONU_FILE_LOCK_OK) {
|
|
_ltype = ltype;
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Following code lifted from http://stackoverflow.com/questions/910528/how-to-change-the-acls-from-c
|
|
#include <Accctrl.h>
|
|
#include <Aclapi.h>
|
|
static bool SetFilePermission(LPCSTR FileName)
|
|
{
|
|
PSID pEveryoneSID = NULL;
|
|
PACL pACL = NULL;
|
|
EXPLICIT_ACCESS ea[1];
|
|
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
|
|
|
// Create a well-known SID for the Everyone group.
|
|
if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
|
|
SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pEveryoneSID))
|
|
return false;
|
|
|
|
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
|
// The ACE will allow Everyone read access to the key.
|
|
ZeroMemory(&ea, 1 * sizeof(EXPLICIT_ACCESS));
|
|
ea[0].grfAccessPermissions = 0xFFFFFFFF;
|
|
ea[0].grfAccessMode = GRANT_ACCESS;
|
|
ea[0].grfInheritance= NO_INHERITANCE;
|
|
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;
|
|
|
|
// Create a new ACL that contains the new ACEs.
|
|
SetLastError(SetEntriesInAcl(1, ea, NULL, &pACL)); // Function actually returns a windows error code
|
|
if (GetLastError() != ERROR_SUCCESS)
|
|
return false;
|
|
|
|
|
|
// Initialize a security descriptor.
|
|
PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
|
|
SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
|
|
if (!InitializeSecurityDescriptor(pSD,SECURITY_DESCRIPTOR_REVISION))
|
|
return false;
|
|
|
|
// Add the ACL to the security descriptor.
|
|
if (!SetSecurityDescriptorDacl(pSD,
|
|
TRUE, // bDaclPresent flag
|
|
pACL,
|
|
FALSE)) // not a default DACL
|
|
return false;
|
|
|
|
|
|
//Change the security attributes
|
|
if (!SetFileSecurity(FileName, DACL_SECURITY_INFORMATION, pSD))
|
|
return false;
|
|
|
|
if (pEveryoneSID)
|
|
FreeSid(pEveryoneSID);
|
|
if (pACL)
|
|
LocalFree(pACL);
|
|
if (pSD)
|
|
LocalFree(pSD);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
IONU_FILE_LOCK_STATUS sequencelogic::LockFileAttempt(const std::string& filename, const std::string& id,IONU_FILE_LOCK_TYPE ltype)
|
|
{
|
|
bool addLock = true;
|
|
bool modified = false;
|
|
IONU_FILE_LOCK_STATUS gotLock = IONU_FILE_LOCK_OK;
|
|
EyeJSONObject* lock = nullptr;
|
|
EyeJSONScalar* lockid = nullptr;
|
|
EyeJSONScalar* lockpid = nullptr;
|
|
EyeJSONScalar* locktype = nullptr;
|
|
EyeJSONScalar* locktime = nullptr;
|
|
char* buffer = nullptr;
|
|
EyeTime now;
|
|
|
|
// Get the pid
|
|
#ifdef WIN32
|
|
DWORD pid = GetCurrentProcessId();
|
|
#else
|
|
pid_t pid = getpid();
|
|
#endif
|
|
stringstream sspid;
|
|
sspid << pid;
|
|
std::string strpid = sspid.str();
|
|
|
|
// Set mutex lock while checking file locks to keep it all thread safe
|
|
pthread_mutex_lock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = "";
|
|
|
|
#ifdef SHARED_MEMORY
|
|
// Open or create shared memory page for locks the first time in this process
|
|
if (hMapFile == INVALID_HANDLE_VALUE) {
|
|
hMapFile = OpenFileMapping(
|
|
FILE_MAP_ALL_ACCESS, // read/write access
|
|
FALSE, // do not inherit the name
|
|
IONU_MASTER_LOCK_SHARED); // name of mapping object
|
|
|
|
// Open failed so create shared memory page for locks
|
|
if (hMapFile == NULL)
|
|
hMapFile = CreateFileMapping(
|
|
INVALID_HANDLE_VALUE, // use paging file
|
|
NULL, // default security
|
|
PAGE_READWRITE, // read/write access
|
|
0, // maximum object size (high-order DWORD)
|
|
IONU_FILE_LOCK_MAX_SIZE, // maximum object size (low-order DWORD)
|
|
IONU_MASTER_LOCK_SHARED); // name of mapping object
|
|
|
|
// Open and Create both failed
|
|
if (hMapFile == NULL) {
|
|
LastEyeLockMessage = "CreateFileMapping() failed ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
pBuf = (LPTSTR) MapViewOfFile (hMapFile, // handle to map object
|
|
FILE_MAP_ALL_ACCESS, // read/write permission
|
|
0, // file offset high-order
|
|
0, // file offset low-order
|
|
IONU_FILE_LOCK_MAX_SIZE);
|
|
|
|
if (pBuf == NULL) {
|
|
LastEyeLockMessage = "MapViewOfFile() failed ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
CloseHandle (hMapFile);
|
|
hMapFile = INVALID_HANDLE_VALUE;
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
// Initialize with null string
|
|
else {
|
|
CopyMemory ((PVOID)pBuf, "", 1);
|
|
}
|
|
}
|
|
buffer = pBuf;
|
|
if (buffer) {
|
|
|
|
#elif defined(WIN32)
|
|
// Read/Create the master lock file
|
|
std::string tempPath = getenv("windir");
|
|
if (tempPath == "") {
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
LastEyeLockMessage = "Failed to get temporary path - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
std::string masterLockFile(tempPath);
|
|
masterLockFile += "\\temp\\";
|
|
masterLockFile += IONU_MASTER_LOCK_FILE;
|
|
HANDLE handle = CreateFile (masterLockFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
DWORD createFileError = GetLastError();
|
|
if (createFileError != ERROR_ALREADY_EXISTS) {
|
|
if (!SetFilePermission(masterLockFile.c_str())) {
|
|
// Flag this in a way that's unambiguously bad!
|
|
DWORD error = GetLastError();
|
|
char lastError[5000];
|
|
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
error,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPTSTR) lastError,
|
|
sizeof(lastError),
|
|
NULL );
|
|
MessageBox(0, lastError, "sequencelogic::LockFileAttempt() unable to set file permissions for Master Lock File", MB_OK);
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(handle);
|
|
handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
else if (createFileError == ERROR_SHARING_VIOLATION) {
|
|
// We expect sharing violations to occur because different processes use the lock file
|
|
// Since we were able to set file permissions, we can retry later and expect eventual success.
|
|
LastEyeLockMessage = "Master lock file sharing violation";
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
return IONU_FILE_LOCK_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
BY_HANDLE_FILE_INFORMATION fileInfo = { 0 };
|
|
GetFileInformationByHandle (handle, &fileInfo);
|
|
DWORD nread = 0;
|
|
if (fileInfo.nFileSizeLow > 0) {
|
|
buffer = (char*)sequencelogic::New ((size_t)fileInfo.nFileSizeLow + 1);
|
|
if (buffer == nullptr) {
|
|
LastEyeLockMessage = "Allocation failed";
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
if (ReadFile (handle, buffer, fileInfo.nFileSizeLow, &nread, NULL) != TRUE) {
|
|
LastEyeLockMessage = "Read failed - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
sequencelogic::RemoveFile (masterLockFile);
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
CloseHandle (handle);
|
|
IONUDEBUG ("EyeFileLock::acquire() ReadFile failed %s", LastEyeLockMessage.c_str());
|
|
delete[] buffer;
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
buffer[nread] = '\0';
|
|
}
|
|
#else
|
|
std::string masterLockFile ("/tmp/");
|
|
masterLockFile += IONU_MASTER_LOCK_FILE;
|
|
fstream file (masterLockFile, ios::in | ios::out | ios::binary | ios::ate);
|
|
if (file.is_open()) {
|
|
size_t bytes = (size_t)file.tellg();
|
|
file.seekg (0, ios::beg);
|
|
buffer = (char*)sequencelogic::New (bytes + 1);
|
|
if (buffer == nullptr) {
|
|
LastEyeLockMessage = "Allocation failed";
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
buffer[bytes] = '\0';
|
|
if (bytes > 0) {
|
|
file.read (buffer, bytes);
|
|
if (file.bad()) {
|
|
LastEyeLockMessage = "Read failed - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
sequencelogic::RemoveFile (masterLockFile);
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
file.close();
|
|
IONUDEBUG ("EyeFileLock::acquire() read failed %s", LastEyeLockMessage.c_str());
|
|
delete[] buffer;
|
|
return IONU_FILE_LOCK_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
file.open (masterLockFile, ios::out | ios::binary | ios::trunc);
|
|
}
|
|
if (file.is_open()) {
|
|
#endif
|
|
EyeJSONObject locks (buffer);
|
|
if (locks.GetNumMembers() > 0) {
|
|
for (int i = (int)locks.GetNumMembers() - 1; i >= 0; --i) {
|
|
lock = dynamic_cast<EyeJSONObject*>(locks.GetMember (i));
|
|
if (lock && lock->GetKey()) {
|
|
// Just in case there are no locks
|
|
if (lock->GetNumMembers() == 0) {
|
|
locks.RemoveMember (i);
|
|
modified = true;
|
|
continue;
|
|
}
|
|
if (filename.compare (lock->GetKey()) == 0) {
|
|
if (ltype == IONU_FILE_LOCK_REMOVE) {
|
|
locks.RemoveMember (i);
|
|
modified = true;
|
|
addLock = false;
|
|
break;
|
|
}
|
|
for (int j = (int)lock->GetNumMembers() - 1; j >= 0; j--) {
|
|
EyeJSONObject* elock = dynamic_cast<EyeJSONObject*>(lock->GetMember (j));
|
|
lockpid = static_cast<EyeJSONScalar*>(elock->GetMember("pid"));
|
|
lockid = static_cast<EyeJSONScalar*>(elock->GetMember("id"));
|
|
locktype = static_cast<EyeJSONScalar*>(elock->GetMember("type"));
|
|
locktime = static_cast<EyeJSONScalar*>(elock->GetMember("time"));
|
|
if (lockpid && lockid && locktype && CheckActiveProcess (lockpid->GetValue())) {
|
|
if (id.compare (lockid->GetValue()) == 0 && strpid.compare (lockpid->GetValue()) == 0) {
|
|
// Clear lock
|
|
if (ltype == IONU_FILE_LOCK_CLEAR) {
|
|
if (lock->GetNumMembers() == 1)
|
|
locks.RemoveMember (i);
|
|
else
|
|
lock->RemoveMember(j);
|
|
modified = true;
|
|
addLock = false;
|
|
break;
|
|
}
|
|
// Already have read lock
|
|
else if (ltype == IONU_FILE_LOCK_READ && strcmp (locktype->GetValue(), "R") == 0) {
|
|
addLock = false;
|
|
break;
|
|
}
|
|
// Already have write lock
|
|
else if (ltype == IONU_FILE_LOCK_WRITE && strcmp (locktype->GetValue(), "W") == 0) {
|
|
addLock = false;
|
|
break;
|
|
}
|
|
// Convert to read lock
|
|
else if (ltype == IONU_FILE_LOCK_READ && strcmp (locktype->GetValue(), "W") == 0) {
|
|
locktype->ReplaceValue ("R");
|
|
modified = true;
|
|
addLock = false;
|
|
break;
|
|
}
|
|
// Convert to write lock, if there are no other locks besides ours
|
|
else if (lock->GetNumMembers() == 1 && ltype == IONU_FILE_LOCK_WRITE && strcmp (locktype->GetValue(), "R") == 0) {
|
|
locktype->ReplaceValue ("W");
|
|
modified = true;
|
|
addLock = false;
|
|
break;
|
|
}
|
|
// Can't get the requested lock
|
|
else {
|
|
gotLock = IONU_FILE_LOCK_TIMEOUT;
|
|
if (strcmp (locktype->GetValue(), "R") == 0)
|
|
LastEyeLockMessage = "Read lock at ";
|
|
else
|
|
LastEyeLockMessage = "Write lock at ";
|
|
LastEyeLockMessage += (locktime ? locktime->GetValue() : "?");
|
|
LastEyeLockMessage += " by: ";
|
|
LastEyeLockMessage += lockpid->GetValue();
|
|
LastEyeLockMessage += ":";
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
addLock = false;
|
|
break;
|
|
}
|
|
}
|
|
// Another owner has a write lock or we want a write lock
|
|
else if (ltype == IONU_FILE_LOCK_WRITE || strcmp (locktype->GetValue(), "W") == 0) {
|
|
gotLock = IONU_FILE_LOCK_TIMEOUT;
|
|
if (strcmp (locktype->GetValue(), "R") == 0)
|
|
LastEyeLockMessage = "Read lock at ";
|
|
else
|
|
LastEyeLockMessage = "Write lock at ";
|
|
LastEyeLockMessage += (locktime ? locktime->GetValue() : "?");
|
|
LastEyeLockMessage += " by: ";
|
|
LastEyeLockMessage += lockpid->GetValue();
|
|
LastEyeLockMessage += ":";
|
|
LastEyeLockMessage += lockid->GetValue();
|
|
addLock = false;
|
|
break;
|
|
}
|
|
}
|
|
// Invalid lock or owner process not running
|
|
else {
|
|
lock->RemoveMember(j);
|
|
modified = true;
|
|
}
|
|
}
|
|
// Add a new read lock to lock array
|
|
if (addLock && gotLock == IONU_FILE_LOCK_OK && ltype == IONU_FILE_LOCK_READ) {
|
|
EyeJSONObject* alock = new EyeJSONObject (nullptr, JSON_OBJECT, lock);
|
|
alock->AddMember (new EyeJSONScalar ("pid", strpid.c_str(), JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("type", "R", JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("time", now.GetISO8601Time(), JSON_STRING));
|
|
lock->AddMember (alock);
|
|
modified = true;
|
|
addLock = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// No existing lock, add ours
|
|
if (addLock && (ltype == IONU_FILE_LOCK_READ || ltype == IONU_FILE_LOCK_WRITE)) {
|
|
EyeJSONObject* array = new EyeJSONObject (filename.c_str(), JSON_ARRAY, &locks);
|
|
EyeJSONObject* alock = new EyeJSONObject (nullptr, JSON_OBJECT, array);
|
|
alock->AddMember (new EyeJSONScalar ("pid", strpid.c_str(), JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("id", id.c_str(), JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("type", ltype == IONU_FILE_LOCK_READ ? "R" : "W", JSON_STRING));
|
|
alock->AddMember (new EyeJSONScalar ("time", now.GetISO8601Time(), JSON_STRING));
|
|
array->AddMember (alock);
|
|
locks.AddMember (array);
|
|
modified = true;
|
|
}
|
|
if (modified && locks.GetNumMembers() >= 1) {
|
|
std::string json = locks.Stringify();
|
|
//cout << json << endl;
|
|
#ifdef SHARED_MEMORY
|
|
// Copy lock data to shared memory
|
|
CopyMemory ((PVOID)pBuf, json.c_str(), json.size() + 1);
|
|
buffer = nullptr;
|
|
//UnmapViewOfFile (pBuf);
|
|
//CloseHandle (hMapFile);
|
|
#elif defined (WIN32)
|
|
DWORD nwritten;
|
|
SetFilePointer (handle, 0, NULL, FILE_BEGIN);
|
|
SetEndOfFile (handle);
|
|
if (WriteFile (handle, json.c_str(), (DWORD)(json.size() + 1), &nwritten, NULL) != TRUE || nwritten != (DWORD)(json.size() + 1)) {
|
|
LastEyeLockMessage = "Write failed - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
sequencelogic::RemoveFile (masterLockFile);
|
|
gotLock = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
if (CloseHandle (handle) == 0) {
|
|
LastEyeLockMessage = "Close failed - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
gotLock = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
#else
|
|
file.seekp (0, ios::beg);
|
|
file.write (json.c_str(), json.size() + 1);
|
|
if (file.bad()) {
|
|
LastEyeLockMessage = "Write failed - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
sequencelogic::RemoveFile (masterLockFile);
|
|
gotLock = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
file.close();
|
|
#endif
|
|
}
|
|
// No more locks or unchanged
|
|
else {
|
|
#ifdef SHARED_MEMORY
|
|
// Done with shared memory, write null string
|
|
if (modified)
|
|
CopyMemory ((PVOID)pBuf, "", 1);
|
|
//UnmapViewOfFile (pBuf);
|
|
//CloseHandle (hMapFile);
|
|
buffer = nullptr;
|
|
#elif defined (WIN32)
|
|
// On Windows we keep the file around
|
|
if (modified) {
|
|
SetFilePointer (handle, 0, NULL, FILE_BEGIN);
|
|
SetEndOfFile (handle);
|
|
}
|
|
CloseHandle (handle);
|
|
#else
|
|
file.close();
|
|
if (modified)
|
|
sequencelogic::RemoveFile (masterLockFile);
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
LastEyeLockMessage = "Open failed - ";
|
|
LastEyeLockMessage += sequencelogic::GetLastErrorMessage();
|
|
IONUDEBUG ("EyeFileLock::acquire() %s", LastEyeLockMessage.c_str());
|
|
gotLock = IONU_FILE_LOCK_ERROR;
|
|
}
|
|
|
|
pthread_mutex_unlock (&ionu_filelock_mutex);
|
|
if (buffer)
|
|
delete[] buffer;
|
|
return gotLock;
|
|
}
|
|
|
|
EyeFileReadLock::EyeFileReadLock (const std::string& filename, const std::string& id)
|
|
:EyeFileLock (IONU_FILE_LOCK_READ, filename, id)
|
|
{
|
|
}
|
|
|
|
EyeFileWriteLock::EyeFileWriteLock (const std::string& filename, const std::string& id)
|
|
:EyeFileLock (IONU_FILE_LOCK_WRITE, filename, id)
|
|
{
|
|
}
|
|
#endif
|