diff --git a/Foundation/Foundation_vs160.vcxproj b/Foundation/Foundation_vs160.vcxproj
index f0d0379f2..3781a6677 100644
--- a/Foundation/Foundation_vs160.vcxproj
+++ b/Foundation/Foundation_vs160.vcxproj
@@ -977,6 +977,35 @@
true
true
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
true
@@ -1613,6 +1642,9 @@
+
+
+
@@ -1891,4 +1923,4 @@
-
\ No newline at end of file
+
diff --git a/Foundation/Foundation_vs170.vcxproj b/Foundation/Foundation_vs170.vcxproj
index 4491d060f..041e45a1e 100644
--- a/Foundation/Foundation_vs170.vcxproj
+++ b/Foundation/Foundation_vs170.vcxproj
@@ -1415,6 +1415,47 @@
true
true
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
true
@@ -2231,6 +2272,9 @@
+
+
+
@@ -2533,4 +2577,4 @@
-
\ No newline at end of file
+
diff --git a/Foundation/Makefile b/Foundation/Makefile
index 67b68bb48..3e07a06d3 100644
--- a/Foundation/Makefile
+++ b/Foundation/Makefile
@@ -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
diff --git a/Foundation/include/Poco/FileStreamRWLock.h b/Foundation/include/Poco/FileStreamRWLock.h
new file mode 100644
index 000000000..7227ed03d
--- /dev/null
+++ b/Foundation/include/Poco/FileStreamRWLock.h
@@ -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
+
+#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
diff --git a/Foundation/include/Poco/FileStreamRWLock_POSIX.h b/Foundation/include/Poco/FileStreamRWLock_POSIX.h
new file mode 100644
index 000000000..9bcd38624
--- /dev/null
+++ b/Foundation/include/Poco/FileStreamRWLock_POSIX.h
@@ -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
+#include
+
+
+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
diff --git a/Foundation/include/Poco/FileStreamRWLock_WIN32.h b/Foundation/include/Poco/FileStreamRWLock_WIN32.h
new file mode 100644
index 000000000..537b19ae2
--- /dev/null
+++ b/Foundation/include/Poco/FileStreamRWLock_WIN32.h
@@ -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
+
+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
diff --git a/Foundation/src/FileStreamRWLock.cpp b/Foundation/src/FileStreamRWLock.cpp
new file mode 100644
index 000000000..482b4933e
--- /dev/null
+++ b/Foundation/src/FileStreamRWLock.cpp
@@ -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
diff --git a/Foundation/src/FileStreamRWLock_POSIX.cpp b/Foundation/src/FileStreamRWLock_POSIX.cpp
new file mode 100644
index 000000000..94c7c8a0b
--- /dev/null
+++ b/Foundation/src/FileStreamRWLock_POSIX.cpp
@@ -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
diff --git a/Foundation/src/FileStreamRWLock_WIN32.cpp b/Foundation/src/FileStreamRWLock_WIN32.cpp
new file mode 100644
index 000000000..12d4935ba
--- /dev/null
+++ b/Foundation/src/FileStreamRWLock_WIN32.cpp
@@ -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
diff --git a/Foundation/testsuite/Makefile-Driver b/Foundation/testsuite/Makefile-Driver
index 2f4396a1f..846c62f84 100644
--- a/Foundation/testsuite/Makefile-Driver
+++ b/Foundation/testsuite/Makefile-Driver
@@ -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
diff --git a/Foundation/testsuite/TestSuite_vs160.vcxproj b/Foundation/testsuite/TestSuite_vs160.vcxproj
index 41a1fa60a..1153387e5 100644
--- a/Foundation/testsuite/TestSuite_vs160.vcxproj
+++ b/Foundation/testsuite/TestSuite_vs160.vcxproj
@@ -663,6 +663,7 @@
+
@@ -806,7 +807,9 @@
+
+
@@ -912,4 +915,4 @@
-
\ No newline at end of file
+
diff --git a/Foundation/testsuite/TestSuite_vs160.vcxproj.filters b/Foundation/testsuite/TestSuite_vs160.vcxproj.filters
index e26995529..e20e0fbfc 100644
--- a/Foundation/testsuite/TestSuite_vs160.vcxproj.filters
+++ b/Foundation/testsuite/TestSuite_vs160.vcxproj.filters
@@ -603,6 +603,8 @@
Threading\Source Files
+
+ Processes\Source Files
Logging\Source Files
@@ -1031,8 +1033,10 @@
Threading\Header Files
+
+ Processes\Header Files
Logging\Header Files
-
\ No newline at end of file
+
diff --git a/Foundation/testsuite/TestSuite_vs170.vcxproj b/Foundation/testsuite/TestSuite_vs170.vcxproj
index 91e3d7910..d3a2a1cd4 100644
--- a/Foundation/testsuite/TestSuite_vs170.vcxproj
+++ b/Foundation/testsuite/TestSuite_vs170.vcxproj
@@ -996,6 +996,7 @@
+
@@ -1140,6 +1141,7 @@
+
@@ -1245,4 +1247,4 @@
-
\ No newline at end of file
+
diff --git a/Foundation/testsuite/TestSuite_vs170.vcxproj.filters b/Foundation/testsuite/TestSuite_vs170.vcxproj.filters
index e26995529..e20e0fbfc 100644
--- a/Foundation/testsuite/TestSuite_vs170.vcxproj.filters
+++ b/Foundation/testsuite/TestSuite_vs170.vcxproj.filters
@@ -603,6 +603,8 @@
Threading\Source Files
+
+ Processes\Source Files
Logging\Source Files
@@ -1031,8 +1033,10 @@
Threading\Header Files
+
+ Processes\Header Files
Logging\Header Files
-
\ No newline at end of file
+
diff --git a/Foundation/testsuite/src/FileStreamRWLockTest.cpp b/Foundation/testsuite/src/FileStreamRWLockTest.cpp
new file mode 100644
index 000000000..2752709ee
--- /dev/null
+++ b/Foundation/testsuite/src/FileStreamRWLockTest.cpp
@@ -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 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 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;
+}
diff --git a/Foundation/testsuite/src/FileStreamRWLockTest.h b/Foundation/testsuite/src/FileStreamRWLockTest.h
new file mode 100644
index 000000000..86d8421ff
--- /dev/null
+++ b/Foundation/testsuite/src/FileStreamRWLockTest.h
@@ -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
diff --git a/Foundation/testsuite/src/ProcessesTestSuite.cpp b/Foundation/testsuite/src/ProcessesTestSuite.cpp
index ab0f12b59..dd9671aaa 100644
--- a/Foundation/testsuite/src/ProcessesTestSuite.cpp
+++ b/Foundation/testsuite/src/ProcessesTestSuite.cpp
@@ -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;
}
diff --git a/Foundation/testsuite/src/TestApp.cpp b/Foundation/testsuite/src/TestApp.cpp
index ff62cd6f2..8c0d0ac8c 100644
--- a/Foundation/testsuite/src/TestApp.cpp
+++ b/Foundation/testsuite/src/TestApp.cpp
@@ -19,10 +19,13 @@
#include
#include
+#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 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(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 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(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;
}