* add RWLock implementation for file [posix]

* add implementation FileStreamRWLock for windows
replace FileStreamRWLock to the Process package

* add files FileStreamRWLock* into makefile and vcproj

* remove unnecessary file from makefile

* use absolute path to the TesApp with ProcessRunner

* fix vc*.proj

* add new test files into vc.proj.filters

* fix comments

* fix spelling fo PR #4723
add atomic_bool _locked and check if FileStreamRWLock is locked on
destruction for force unlock

* add atomic header

* File lock (#4740)

* throw error on any errno not only on EDEADLK

* fix function naming

* fix windows build

* fix windows build

---------

Co-authored-by: Alexander B <ale.bychuk@gmail.com>
Co-authored-by: bas524 <bas524@ya.ru>
This commit is contained in:
Aleksandar Fabijanic 2024-10-16 16:58:00 -05:00 committed by GitHub
parent 6f34ec89ce
commit e5752a5c6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1141 additions and 8 deletions

View File

@ -977,6 +977,35 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\FileStreamRWLock.cpp" />
<ClCompile Include="src\FileStreamRWLock_POSIX.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\FileStreamRWLock_WIN32.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\NamedMutex.cpp" />
<ClCompile Include="src\NamedMutex_UNIX.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|Win32'">true</ExcludedFromBuild>
@ -1613,6 +1642,9 @@
<ClInclude Include="include\Poco\FileStreamFactory.h" />
<ClInclude Include="include\Poco\FileStream_POSIX.h" />
<ClInclude Include="include\Poco\FileStream_WIN32.h" />
<ClInclude Include="include\Poco\FileStreamRWLock.h" />
<ClInclude Include="include\Poco\FileStreamRWLock_POSIX.h" />
<ClInclude Include="include\Poco\FileStreamRWLock_WIN32.h" />
<ClInclude Include="include\Poco\File_UNIX.h" />
<ClInclude Include="include\Poco\File_WIN32U.h" />
<ClInclude Include="include\Poco\Format.h" />
@ -1891,4 +1923,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -1415,6 +1415,47 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\FileStreamRWLock.cpp" />
<ClCompile Include="src\FileStreamRWLock_POSIX.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\FileStreamRWLock_WIN32.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_md|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_static_mt|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_shared|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_md|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release_static_mt|ARM64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\NamedMutex.cpp" />
<ClCompile Include="src\NamedMutex_UNIX.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug_shared|Win32'">true</ExcludedFromBuild>
@ -2231,6 +2272,9 @@
<ClInclude Include="include\Poco\FileStreamFactory.h" />
<ClInclude Include="include\Poco\FileStream_POSIX.h" />
<ClInclude Include="include\Poco\FileStream_WIN32.h" />
<ClInclude Include="include\Poco\FileStreamRWLock.h" />
<ClInclude Include="include\Poco\FileStreamRWLock_POSIX.h" />
<ClInclude Include="include\Poco\FileStreamRWLock_WIN32.h" />
<ClInclude Include="include\Poco\File_UNIX.h" />
<ClInclude Include="include\Poco\File_WIN32U.h" />
<ClInclude Include="include\Poco\Format.h" />
@ -2533,4 +2577,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -34,7 +34,7 @@ objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel AsyncNotificationCent
FileStreamFactory URIStreamFactory URIStreamOpener UTF32Encoding UTF16Encoding UTF8Encoding UTF8String \
Unicode UnicodeConverter Windows1250Encoding Windows1251Encoding Windows1252Encoding \
UUID UUIDGenerator Void Var VarHolder VarIterator VarVisitor Format Pipe PipeImpl PipeStream SharedMemory \
MemoryStream FileStream AtomicCounter DataURIStream DataURIStreamFactory
MemoryStream FileStream AtomicCounter DataURIStream DataURIStreamFactory FileStreamRWLock
zlib_objects = adler32 compress crc32 deflate \
infback inffast inflate inftrees trees zutil

View File

@ -0,0 +1,204 @@
//
// FileStreamRWLock.h
//
// Library: Foundation
// Package: Processes
// Module: FileStreamRWLock
//
// Definition of the FileStreamRWLock class.
//
// Copyright (c) 2004-2024, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Foundation_FileStreamRWLock_INCLUDED
#define Foundation_FileStreamRWLock_INCLUDED
#include "Poco/Foundation.h"
#include "Poco/Exception.h"
#include "Poco/FileStream.h"
#include <atomic>
#if defined(POCO_OS_FAMILY_WINDOWS)
#include "Poco/FileStreamRWLock_WIN32.h"
#else
#include "Poco/FileStreamRWLock_POSIX.h"
#endif
namespace Poco {
class ScopedFStreamRWLock;
class ScopedFStreamReadRWLock;
class ScopedFStreamWriteRWLock;
class Foundation_API FileStreamRWLock: private FileStreamRWLockImpl
/// A reader writer lock on the file region allows multiple concurrent
/// process-readers or one exclusive process-writer.
{
public:
using ScopedLock = ScopedFStreamRWLock;
using ScopedReadLock = ScopedFStreamReadRWLock;
using ScopedWriteLock = ScopedFStreamWriteRWLock;
FileStreamRWLock(const FileStream &fs, Poco::UInt64 offset, Poco::UInt64 size);
/// Creates the Reader/Writer lock on the file region.
/// offset - from start of the file
/// size - size of the locker region
~FileStreamRWLock();
/// Destroys the Reader/Writer lock on the file region.
void readLock();
/// Acquires a read lock. If another process currently holds a write lock,
/// waits until the write lock is released.
bool tryReadLock();
/// Tries to acquire a read lock. Immediately returns true if successful, or
/// false if another process currently holds a write lock.
void writeLock();
/// Acquires a write lock on the file region. If one or more other processes currently hold
/// locks, waits until all locks are released.
bool tryWriteLock();
/// Tries to acquire a write lock on the file region. Immediately returns true if successful,
/// or false if one or more other processes currently hold
/// locks.
void unlock();
/// Releases the read or write lock.
private:
std::atomic_bool _locked = false;
FileStreamRWLock(const FileStreamRWLock&);
FileStreamRWLock& operator = (const FileStreamRWLock&);
};
class Foundation_API ScopedFStreamRWLock
/// A variant of ScopedLock for reader/writer locks.
{
public:
ScopedFStreamRWLock(FileStreamRWLock& rwl, bool write = false);
~ScopedFStreamRWLock();
private:
FileStreamRWLock& _rwl;
ScopedFStreamRWLock();
ScopedFStreamRWLock(const ScopedFStreamRWLock&);
ScopedFStreamRWLock& operator = (const ScopedFStreamRWLock&);
};
class Foundation_API ScopedFStreamReadRWLock : public ScopedFStreamRWLock
/// A variant of ScopedLock for reader locks.
{
public:
ScopedFStreamReadRWLock(FileStreamRWLock& rwl);
~ScopedFStreamReadRWLock();
};
class Foundation_API ScopedFStreamWriteRWLock : public ScopedFStreamRWLock
/// A variant of ScopedLock for writer locks.
{
public:
ScopedFStreamWriteRWLock(FileStreamRWLock& rwl);
~ScopedFStreamWriteRWLock();
};
//
// inlines
//
inline void FileStreamRWLock::readLock()
{
readLockImpl();
_locked = true;
}
inline bool FileStreamRWLock::tryReadLock()
{
bool locked = tryReadLockImpl();
if (locked) _locked = true; // assign only if success lock
return locked;
}
inline void FileStreamRWLock::writeLock()
{
writeLockImpl();
_locked = true;
}
inline bool FileStreamRWLock::tryWriteLock()
{
bool locked = tryWriteLockImpl();
if (locked) _locked = true; // assign only if success lock
return locked;
}
inline void FileStreamRWLock::unlock()
{
unlockImpl();
_locked = false;
}
inline ScopedFStreamRWLock::ScopedFStreamRWLock(FileStreamRWLock& rwl, bool write): _rwl(rwl)
{
if (write)
_rwl.writeLock();
else
_rwl.readLock();
}
inline ScopedFStreamRWLock::~ScopedFStreamRWLock()
{
try
{
_rwl.unlock();
}
catch (...)
{
poco_unexpected();
}
}
inline ScopedFStreamReadRWLock::ScopedFStreamReadRWLock(FileStreamRWLock& rwl): ScopedFStreamRWLock(rwl, false)
{
}
inline ScopedFStreamReadRWLock::~ScopedFStreamReadRWLock()
{
}
inline ScopedFStreamWriteRWLock::ScopedFStreamWriteRWLock(FileStreamRWLock& rwl): ScopedFStreamRWLock(rwl, true)
{
}
inline ScopedFStreamWriteRWLock::~ScopedFStreamWriteRWLock()
{
}
} // namespace Poco
#endif // Foundation_FileStreamRWLock_INCLUDED

View File

@ -0,0 +1,115 @@
//
// FileStreamRWLock_POSIX.h
//
// Library: Foundation
// Package: Processes
// Module: FileStreamRWLock
//
// Definition of the FileStreamRWLockImpl class for POSIX FileStream.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Foundation_FileStreamRWLock_POSIX_INCLUDED
#define Foundation_FileStreamRWLock_POSIX_INCLUDED
#include "Poco/Foundation.h"
#include "Poco/Exception.h"
#include "Poco/FileStream.h"
#include <fcntl.h>
#include <cerrno>
namespace Poco {
class Foundation_API FileStreamRWLockImpl
{
protected:
FileStreamRWLockImpl(const FileStream::NativeHandle &fd, Poco::UInt64 offset, Poco::UInt64 size);
~FileStreamRWLockImpl();
void readLockImpl();
bool tryReadLockImpl();
void writeLockImpl();
bool tryWriteLockImpl();
void unlockImpl();
private:
FileStream::NativeHandle _fd;
struct flock _flock;
int _lockMode;
};
//
// inlines
//
inline void FileStreamRWLockImpl::readLockImpl()
{
_flock.l_type = F_RDLCK;
_lockMode = F_SETLKW;
int rc = fcntl(_fd, _lockMode, &_flock);
if (rc == -1)
throw SystemException("cannot lock reader lock", errno);
}
inline bool FileStreamRWLockImpl::tryReadLockImpl()
{
_flock.l_type = F_RDLCK;
_lockMode = F_SETLK;
int rc = fcntl(_fd, _lockMode, &_flock);
if (rc == 0)
return true;
else if (errno == EAGAIN || errno == EACCES)
return false;
else
throw SystemException("cannot lock try-reader lock", errno);
}
inline void FileStreamRWLockImpl::writeLockImpl()
{
_flock.l_type = F_WRLCK;
_lockMode = F_SETLKW;
int rc = fcntl(_fd, _lockMode, &_flock);
if (rc == -1)
throw SystemException("cannot lock writer lock", errno);
}
inline bool FileStreamRWLockImpl::tryWriteLockImpl()
{
_flock.l_type = F_WRLCK;
_lockMode = F_SETLK;
int rc = fcntl(_fd, _lockMode, &_flock);
if (rc != -1)
return true;
else if (errno == EAGAIN || errno == EACCES)
return false;
else
throw SystemException("cannot lock writer lock", errno);
}
inline void FileStreamRWLockImpl::unlockImpl()
{
_flock.l_type = F_UNLCK;
_lockMode = F_SETLKW;
int rc = fcntl(_fd, _lockMode, &_flock);
if (rc == -1)
throw SystemException("cannot unlock", errno);
}
} // namespace Poco
#endif // Foundation_FileStreamRWLock_POSIX_INCLUDED

View File

@ -0,0 +1,134 @@
//
// FileStreamRWLock_WIN32.h
//
// Library: Foundation
// Package: Processes
// Module: FileStreamRWLock
//
// Definition of the FileStreamRWLockImpl class for WIN32 FileStream.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Foundation_FileStreamRWLock_WIN32_INCLUDED
#define Foundation_FileStreamRWLock_WIN32_INCLUDED
#include "Poco/Foundation.h"
#include "Poco/Exception.h"
#include "Poco/FileStream.h"
#include <Windows.h>
namespace Poco {
struct LockMode
{
static constexpr DWORD READ = 0;
static constexpr DWORD WRITE = LOCKFILE_EXCLUSIVE_LOCK;
static constexpr DWORD TRY_READ = LOCKFILE_FAIL_IMMEDIATELY;
static constexpr DWORD TRY_WRITE = (LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY);
};
class Foundation_API FileStreamRWLockImpl
{
protected:
FileStreamRWLockImpl(const FileStream::NativeHandle &fd, Poco::UInt64 offset, Poco::UInt64 size);
~FileStreamRWLockImpl();
void readLockImpl();
bool tryReadLockImpl();
void writeLockImpl();
bool tryWriteLockImpl();
void unlockImpl();
private:
FileStream::NativeHandle _fd;
OVERLAPPED _overlapped;
LARGE_INTEGER _size;
};
//
// inlines
//
inline void FileStreamRWLockImpl::readLockImpl()
{
BOOL fSuccess = LockFileEx(_fd, LockMode::READ, 0, _size.LowPart, _size.HighPart, &_overlapped);
if (!fSuccess)
{
throw SystemException("cannot lock reader lock", GetLastError());
}
}
inline bool FileStreamRWLockImpl::tryReadLockImpl()
{
BOOL fSuccess = LockFileEx(_fd, LockMode::TRY_READ, 0, _size.LowPart, _size.HighPart, &_overlapped);
if (fSuccess)
{
return true;
}
else
{
auto lastError = GetLastError();
if (lastError == ERROR_IO_PENDING || lastError == ERROR_LOCK_VIOLATION)
{
return false;
}
else
{
throw SystemException("cannot lock try-reader lock", lastError);
}
}
}
inline void FileStreamRWLockImpl::writeLockImpl()
{
BOOL fSuccess = LockFileEx(_fd, LockMode::WRITE, 0, _size.LowPart, _size.HighPart, &_overlapped);
if (!fSuccess)
{
throw SystemException("cannot lock writer lock", GetLastError());
}
}
inline bool FileStreamRWLockImpl::tryWriteLockImpl()
{
BOOL fSuccess = LockFileEx(_fd, LockMode::TRY_WRITE, 0, _size.LowPart, _size.HighPart, &_overlapped);
if (fSuccess)
{
return true;
}
else
{
auto lastError = GetLastError();
if (lastError == ERROR_IO_PENDING || lastError == ERROR_LOCK_VIOLATION)
{
return false;
}
else
{
throw SystemException("cannot lock try-writer lock", lastError);
}
}
}
inline void FileStreamRWLockImpl::unlockImpl()
{
BOOL fSuccess = UnlockFileEx(_fd, 0, _size.LowPart, _size.HighPart, &_overlapped);
if (!fSuccess)
{
throw SystemException("cannot unlock ", GetLastError());
}
}
} // namespace Poco
#endif // Foundation_FileStreamRWLock_WIN32_INCLUDED

View File

@ -0,0 +1,44 @@
//
// FileStreamRWLock.cpp
//
// Library: Foundation
// Package: Processes
// Module: FileStreamRWLock
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/FileStreamRWLock.h"
#if defined(POCO_OS_FAMILY_WINDOWS)
#include "FileStreamRWLock_WIN32.cpp"
#else
#include "FileStreamRWLock_POSIX.cpp"
#endif
namespace Poco {
FileStreamRWLock::FileStreamRWLock(const FileStream &fs, Poco::UInt64 offset, Poco::UInt64 size) :
FileStreamRWLockImpl(fs.nativeHandle(), offset, size)
{
}
FileStreamRWLock::~FileStreamRWLock()
{
if (_locked)
{
unlockImpl();
_locked = false;
}
}
} // namespace Poco

View File

@ -0,0 +1,36 @@
//
// FileStreamRWLock_POSIX.cpp
//
// Library: Foundation
// Package: Processes
// Module: FileStreamRWLock
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/FileStreamRWLock_POSIX.h"
namespace Poco {
FileStreamRWLockImpl::FileStreamRWLockImpl(const FileStream::NativeHandle &fd, Poco::UInt64 offset, Poco::UInt64 size):
_fd(fd), _lockMode(0)
{
_flock.l_whence = SEEK_SET;
_flock.l_start = offset;
_flock.l_len = size;
_flock.l_pid = 0;
}
FileStreamRWLockImpl::~FileStreamRWLockImpl()
{
}
} // namespace Poco

View File

@ -0,0 +1,39 @@
//
// FileStreamRWLock_WIN32.cpp
//
// Library: Foundation
// Package: Processes
// Module: FileStreamRWLock
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/FileStreamRWLock_WIN32.h"
namespace Poco {
FileStreamRWLockImpl::FileStreamRWLockImpl(const FileStream::NativeHandle &fd, Poco::UInt64 offset, Poco::UInt64 size):
_fd(fd)
{
LARGE_INTEGER offt;
offt.QuadPart = offset;
memset(&_overlapped, 0, sizeof(OVERLAPPED));
_overlapped.Offset = offt.LowPart;
_overlapped.OffsetHigh = offt.HighPart;
_size.QuadPart = size;
}
FileStreamRWLockImpl::~FileStreamRWLockImpl()
{
}
} // namespace Poco

View File

@ -38,7 +38,7 @@ objects = ActiveMethodTest ActivityTest ActiveDispatcherTest \
UniqueExpireCacheTest UniqueExpireLRUCacheTest UnicodeConverterTest \
TuplesTest NamedTuplesTest TypeListTest VarTest DynamicTestSuite FileStreamTest \
MemoryStreamTest ObjectPoolTest DirectoryWatcherTest DirectoryIteratorsTest \
DataURIStreamTest
DataURIStreamTest FileStreamRWLockTest
target = testrunner
target_version = 1

View File

@ -663,6 +663,7 @@
<ClCompile Include="src\FileChannelTest.cpp" />
<ClCompile Include="src\FileStreamTest.cpp" />
<ClCompile Include="src\FilesystemTestSuite.cpp" />
<ClCompile Include="src\FileStreamRWLockTest.cpp" />
<ClCompile Include="src\FileTest.cpp" />
<ClCompile Include="src\FormatTest.cpp" />
<ClCompile Include="src\FoundationTestSuite.cpp" />
@ -806,7 +807,9 @@
<ClInclude Include="src\FIFOEventTest.h" />
<ClInclude Include="src\FileChannelTest.h" />
<ClInclude Include="src\FileStreamTest.h" />
<ClInclude Include="src\FileStreamRWLockTest.h" />
<ClInclude Include="src\FilesystemTestSuite.h" />
<ClInclude Include="src\FilesystemRWLockTest.h" />
<ClInclude Include="src\FileTest.h" />
<ClInclude Include="src\FormatTest.h" />
<ClInclude Include="src\FoundationTestSuite.h" />
@ -912,4 +915,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -603,6 +603,8 @@
<ClCompile Include="src\ActiveThreadPoolTest.cpp">
<Filter>Threading\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\FileStreamRWLockTest.cpp">
<Filter>Processes\Source Files</Filter>
<ClCompile Include="src\JSONFormatterTest.cpp">
<Filter>Logging\Source Files</Filter>
</ClCompile>
@ -1031,8 +1033,10 @@
<ClInclude Include="src\ActiveThreadPoolTest.h">
<Filter>Threading\Header Files</Filter>
</ClInclude>
<ClInclude Include="src\FileStreamRWLockTest.h">
<Filter>Processes\Header Files</Filter>
<ClInclude Include="src\JSONFormatterTest.h">
<Filter>Logging\Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>

View File

@ -996,6 +996,7 @@
<ClCompile Include="src\FileChannelTest.cpp" />
<ClCompile Include="src\FileStreamTest.cpp" />
<ClCompile Include="src\FilesystemTestSuite.cpp" />
<ClCompile Include="src\FileStreamRWLockTest.cpp" />
<ClCompile Include="src\FileTest.cpp" />
<ClCompile Include="src\FormatTest.cpp" />
<ClCompile Include="src\FoundationTestSuite.cpp" />
@ -1140,6 +1141,7 @@
<ClInclude Include="src\FileChannelTest.h" />
<ClInclude Include="src\FileStreamTest.h" />
<ClInclude Include="src\FilesystemTestSuite.h" />
<ClInclude Include="src\FileStreamRWLockTest.h" />
<ClInclude Include="src\FileTest.h" />
<ClInclude Include="src\FormatTest.h" />
<ClInclude Include="src\FoundationTestSuite.h" />
@ -1245,4 +1247,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -603,6 +603,8 @@
<ClCompile Include="src\ActiveThreadPoolTest.cpp">
<Filter>Threading\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\FileStreamRWLockTest.cpp">
<Filter>Processes\Source Files</Filter>
<ClCompile Include="src\JSONFormatterTest.cpp">
<Filter>Logging\Source Files</Filter>
</ClCompile>
@ -1031,8 +1033,10 @@
<ClInclude Include="src\ActiveThreadPoolTest.h">
<Filter>Threading\Header Files</Filter>
</ClInclude>
<ClInclude Include="src\FileStreamRWLockTest.h">
<Filter>Processes\Header Files</Filter>
<ClInclude Include="src\JSONFormatterTest.h">
<Filter>Logging\Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>

View File

@ -0,0 +1,264 @@
//
// FileStreamRWLockTest.cpp
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "FileStreamRWLockTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/FileStreamRWLock.h"
#include "Poco/FileStream.h"
#include "Poco/TemporaryFile.h"
#include "Poco/Thread.h"
#include "Poco/Runnable.h"
#include "Poco/ProcessRunner.h"
#include "Poco/Path.h"
#include "Poco/Stopwatch.h"
using Poco::FileStreamRWLock;
using Poco::Thread;
using Poco::Runnable;
using Poco::FileStream;
using Poco::TemporaryFile;
using Poco::ProcessRunner;
using Poco::Path;
using Poco::Stopwatch;
static std::string getTestAppName()
{
std::string name("TestApp");
std::string ext;
#if POCO_OS == POCO_OS_WINDOWS_NT
ext = ".exe";
#endif // POCO_OS == POCO_OS_WINDOWS_NT
#if defined(_DEBUG)
Poco::File testapp(name + ext);
if (!testapp.exists())
{
name += "d";
}
#endif
Poco::Path testAppPath = Poco::Path::current();
testAppPath.append(name + ext).makeFile();
return testAppPath.toString();
}
#if POCO_OS == POCO_OS_WINDOWS_NT
#include "Poco/File_WIN32U.h"
HANDLE createFileWithRWAccess(const std::string &path)
{
DWORD access = GENERIC_READ | GENERIC_WRITE;
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
HANDLE handle = CreateFileA(path.c_str(), access, shareMode, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
Poco::File::handleLastError(path);
return handle;
}
#endif
class FileStreamRWLockRunnable: public Runnable
{
public:
FileStreamRWLockRunnable(std::string fileLock): _fileLock(std::move(fileLock)), _cmd(getTestAppName())
{
}
void run() override
{
std::string pid = Poco::format("%spid%ld.file", Path::tempHome(), Thread::currentOsTid());
std::vector<std::string> args;
args.push_back(std::string("--lock-file=").append(_fileLock));
args.push_back(std::string("--pidfile=").append(pid));
ProcessRunner pr(_cmd, args, pid, 0, 10, true);
while (pr.running())
{
Poco::Thread::sleep(100);
}
int result = pr.result();
_ok = (result >= 0);
}
bool ok() const
{
return _ok;
}
private:
std::string _fileLock;
bool _ok{false};
std::string _cmd;
};
class FileStreamRWTryLockRunnable: public Runnable
{
public:
FileStreamRWTryLockRunnable(std::string fileLock): _fileLock(std::move(fileLock)), _cmd(getTestAppName())
{
}
void run() override
{
std::string pid = Poco::format("%spid%ld.file", Path::tempHome(), Thread::currentOsTid());
std::vector<std::string> args;
args.push_back(std::string("--trylock-file=").append(_fileLock));
args.push_back(std::string("--pidfile=").append(pid));
ProcessRunner pr(_cmd, args, pid, 0, 10, true);
while (pr.running())
{
Poco::Thread::sleep(100);
}
int result = pr.result();
_ok = (result >= 0);
}
bool ok() const
{
return _ok;
}
private:
std::string _fileLock;
bool _ok{false};
std::string _cmd;
};
FileStreamRWLockTest::FileStreamRWLockTest(const std::string& name): CppUnit::TestCase(name)
{
}
FileStreamRWLockTest::~FileStreamRWLockTest()
{
}
void FileStreamRWLockTest::testFSLock()
{
TemporaryFile fl;
#if POCO_OS != POCO_OS_WINDOWS_NT
FileStream fs(fl.path(), std::ios::in | std::ios::out | std::ios::binary);
#else
FileStream fs;
fs.openHandle(createFileWithRWAccess(fl.path()), std::ios::in | std::ios::out | std::ios::binary);
#endif // POCO_OS != POCO_OS_WINDOWS_NT
Poco::Int32 i32 = 0;
fs.seekp(0, std::ios::beg);
fs.write((const char *)&i32, sizeof(i32));
fs.flushToDisk();
const auto &path = fl.path();
FileStreamRWLockRunnable r1(path);
FileStreamRWLockRunnable r2(path);
FileStreamRWLockRunnable r3(path);
FileStreamRWLockRunnable r4(path);
FileStreamRWLockRunnable r5(path);
Thread t1;
Thread t2;
Thread t3;
Thread t4;
Thread t5;
t1.start(r1);
t2.start(r2);
t3.start(r3);
t4.start(r4);
t5.start(r5);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
fs.seekp(0, std::ios::beg);
fs.read((char *)&i32, sizeof(i32));
assertEqual(500, i32);
assertTrue (r1.ok());
assertTrue (r2.ok());
assertTrue (r3.ok());
assertTrue (r4.ok());
assertTrue (r5.ok());
}
void FileStreamRWLockTest::testFSTryLock()
{
TemporaryFile fl;
#if POCO_OS != POCO_OS_WINDOWS_NT
FileStream fs(fl.path(), std::ios::in | std::ios::out | std::ios::binary);
#else
FileStream fs;
fs.openHandle(createFileWithRWAccess(fl.path()), std::ios::in | std::ios::out | std::ios::binary);
#endif // POCO_OS != POCO_OS_WINDOWS_NT
Poco::Int32 i32 = 0;
fs.seekp(0, std::ios::beg);
fs.write((const char *)&i32, sizeof(i32));
fs.flushToDisk();
const auto &path = fl.path();
FileStreamRWTryLockRunnable r1(path);
FileStreamRWTryLockRunnable r2(path);
FileStreamRWTryLockRunnable r3(path);
FileStreamRWTryLockRunnable r4(path);
FileStreamRWTryLockRunnable r5(path);
Thread t1;
Thread t2;
Thread t3;
Thread t4;
Thread t5;
t1.start(r1);
t2.start(r2);
t3.start(r3);
t4.start(r4);
t5.start(r5);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
fs.seekp(0, std::ios::beg);
fs.read((char *)&i32, sizeof(i32));
assertEqual(500, i32);
assertTrue (r1.ok());
assertTrue (r2.ok());
assertTrue (r3.ok());
assertTrue (r4.ok());
assertTrue (r5.ok());
}
void FileStreamRWLockTest::setUp()
{
}
void FileStreamRWLockTest::tearDown()
{
}
CppUnit::Test* FileStreamRWLockTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileStreamRWLockTest");
CppUnit_addLongTest(pSuite, FileStreamRWLockTest, testFSLock);
CppUnit_addLongTest(pSuite, FileStreamRWLockTest, testFSTryLock);
return pSuite;
}

View File

@ -0,0 +1,39 @@
//
// FileStreamRWLockTest.h
//
// Definition of the FileStreamRWLockTest class.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef FileStreamRWLockTest_INCLUDED
#define FileStreamRWLockTest_INCLUDED
#include "Poco/Foundation.h"
#include "CppUnit/TestCase.h"
class FileStreamRWLockTest: public CppUnit::TestCase
{
public:
FileStreamRWLockTest(const std::string& name);
~FileStreamRWLockTest();
void testFSLock();
void testFSTryLock();
void setUp();
void tearDown();
static CppUnit::Test* suite();
private:
};
#endif // FileStreamRWLockTest_INCLUDED

View File

@ -14,6 +14,7 @@
#include "NamedEventTest.h"
#include "SharedMemoryTest.h"
#include "ProcessRunnerTest.h"
#include "FileStreamRWLockTest.h"
CppUnit::Test* ProcessesTestSuite::suite()
@ -25,6 +26,7 @@ CppUnit::Test* ProcessesTestSuite::suite()
pSuite->addTest(NamedEventTest::suite());
pSuite->addTest(SharedMemoryTest::suite());
pSuite->addTest(ProcessRunnerTest::suite());
pSuite->addTest(FileStreamRWLockTest::suite());
return pSuite;
}

View File

@ -19,10 +19,13 @@
#include <cstdlib>
#include <signal.h>
#include "Poco/FileStreamRWLock.h"
#if defined(POCO_OS_FAMILY_UNIX)
#include "Poco/Thread.h"
#include "Poco/Runnable.h"
#elif defined(POCO_OS_FAMILY_WINDOWS)
#include "Poco/Thread.h"
#include "Poco/Process.h"
#include "Poco/Event.h"
#include "Poco/NamedEvent.h"
@ -98,6 +101,7 @@ public:
_terminate.wait();
_terminated.set();
}
#elif defined(POCO_OS_FAMILY_UNIX)
void waitForTerminationRequest()
{
@ -156,6 +160,22 @@ public:
#if defined(POCO_OS_FAMILY_WINDOWS)
Poco::Event MyApp::_terminated;
Poco::NamedEvent MyApp::_terminate(Poco::ProcessImpl::terminationEventName(Poco::Process::id()));
#include "Poco/File.h"
#include "Poco/File_WIN32U.h"
HANDLE openFileWithRWAccess(const std::string& path)
{
DWORD access = GENERIC_READ | GENERIC_WRITE;
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
HANDLE handle = CreateFileA(path.c_str(), access, shareMode, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
Poco::File::handleLastError(path);
return handle;
}
#endif
int main(int argc, char** argv)
@ -222,6 +242,153 @@ int main(int argc, char** argv)
}
}
#endif
else if (argc > 2 && arg.find("--lock-file") != std::string::npos && std::string(argv[2]).find("--pidfile") != std::string::npos)
{
std::string pidfArg = std::string(argv[2]);
std::unique_ptr<PIDFile> pidF;
size_t equals_pos = pidfArg.find('=');
if (equals_pos != std::string::npos)
{
std::string pidPath = pidfArg.substr(equals_pos + 1);
pidF = std::make_unique<PIDFile>(pidPath, true);
}
if (pidF == nullptr)
{
return -1;
}
equals_pos = arg.find('=');
if (equals_pos != std::string::npos)
{
std::string fl = arg.substr(equals_pos + 1);
#if POCO_OS != POCO_OS_WINDOWS_NT
FileStream fs(fl, std::ios::in | std::ios::out | std::ios::binary);
#else
FileStream fs;
fs.openHandle(openFileWithRWAccess(fl), std::ios::in | std::ios::out | std::ios::binary);
#endif // POCO_OS != POCO_OS_WINDOWS_NT
Poco::Int32 ok = 1;
Poco::Int32 lastCount = 0;
Poco::Int32 counter = 0;
FileStreamRWLock lock(fs, 0, sizeof(counter));
for (int i = 0; i < 100; ++i)
{
lock.readLock();
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
lastCount = counter;
for (int k = 0; k < 100; ++k)
{
if (counter != lastCount) ok = -1;
}
lock.unlock();
lock.writeLock();
for (int k = 0; k < 100; ++k)
{
counter = 0;
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
--counter;
fs.seekp(0, std::ios::beg);
fs.write((char *)&counter, sizeof(counter));
fs.flushToDisk();
}
for (int k = 0; k < 100; ++k)
{
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
++counter;
fs.seekp(0, std::ios::beg);
fs.write((char *)&counter, sizeof(counter));
fs.flushToDisk();
}
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
++counter;
fs.seekp(0, std::ios::beg);
fs.write((char *)&counter, sizeof(counter));
fs.flushToDisk();
if (counter <= lastCount) ok = -1;
lock.unlock();
}
return ok * counter;
}
return -1;
}
else if (argc > 2 && arg.find("--trylock-file") != std::string::npos && std::string(argv[2]).find("--pidfile") != std::string::npos)
{
std::string pidfArg = std::string(argv[2]);
std::unique_ptr<PIDFile> pidF;
size_t equals_pos = pidfArg.find('=');
if (equals_pos != std::string::npos)
{
std::string pidPath = pidfArg.substr(equals_pos + 1);
pidF = std::make_unique<PIDFile>(pidPath, true);
}
if (pidF == nullptr)
{
return -1;
}
equals_pos = arg.find('=');
if (equals_pos != std::string::npos)
{
std::string fl = arg.substr(equals_pos + 1);
#if POCO_OS != POCO_OS_WINDOWS_NT
FileStream fs(fl, std::ios::in | std::ios::out | std::ios::binary);
#else
FileStream fs;
fs.openHandle(openFileWithRWAccess(fl), std::ios::in | std::ios::out | std::ios::binary);
#endif // POCO_OS != POCO_OS_WINDOWS_NT
Poco::Int32 ok = 1;
Poco::Int32 lastCount = 0;
Poco::Int32 counter = 0;
FileStreamRWLock lock(fs, 0, sizeof(counter));
for (int i = 0; i < 100; ++i)
{
while (!lock.tryReadLock()) Thread::yield();
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
lastCount = counter;
for (int k = 0; k < 100; ++k)
{
if (counter != lastCount) ok = -1;
Thread::yield();
}
lock.unlock();
while (!lock.tryWriteLock()) Thread::yield();
for (int k = 0; k < 100; ++k)
{
counter = 0;
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
--counter;
fs.seekp(0, std::ios::beg);
fs.write((char *)&counter, sizeof(counter));
fs.flushToDisk();
Thread::yield();
}
for (int k = 0; k < 100; ++k)
{
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
++counter;
fs.seekp(0, std::ios::beg);
fs.write((char *)&counter, sizeof(counter));
fs.flushToDisk();
Thread::yield();
}
fs.seekg(0, std::ios::beg);
fs.read((char *)&counter, sizeof(counter));
++counter;
fs.seekp(0, std::ios::beg);
fs.write((char *)&counter, sizeof(counter));
fs.flushToDisk();
if (counter <= lastCount) ok = -1;
lock.unlock();
}
return ok * counter;
}
return -1;
}
}
return argc - 1;
}