diff --git a/Sources/Plasma/CoreLib/CMakeLists.txt b/Sources/Plasma/CoreLib/CMakeLists.txt
index bf6b0e1464..539cdf4b3c 100644
--- a/Sources/Plasma/CoreLib/CMakeLists.txt
+++ b/Sources/Plasma/CoreLib/CMakeLists.txt
@@ -6,6 +6,7 @@ set(CoreLib_SOURCES
hsExceptions.cpp
hsExceptionStack.cpp
hsFastMath.cpp
+ hsFILELock.cpp
hsGeometry3.cpp
hsMatrix33.cpp
hsMatrix44.cpp
@@ -43,6 +44,7 @@ set(CoreLib_HEADERS
hsExceptions.h
hsExceptionStack.h
hsFastMath.h
+ hsFILELock.h
hsGeometry3.h
hsLockGuard.h
hsMatrix44.h
diff --git a/Sources/Plasma/CoreLib/hsFILELock.cpp b/Sources/Plasma/CoreLib/hsFILELock.cpp
new file mode 100644
index 0000000000..9733b774b0
--- /dev/null
+++ b/Sources/Plasma/CoreLib/hsFILELock.cpp
@@ -0,0 +1,162 @@
+/*==LICENSE==*
+
+CyanWorlds.com Engine - MMOG client, server and tools
+Copyright (C) 2011 Cyan Worlds, Inc.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+Additional permissions under GNU GPL version 3 section 7
+
+If you modify this Program, or any covered work, by linking or
+combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
+NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
+JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
+(or a modified version of those libraries),
+containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
+PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
+JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
+licensors of this Program grant you additional
+permission to convey the resulting work. Corresponding Source for a
+non-source form of such a combination shall include the source code for
+the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
+work.
+
+You can contact Cyan Worlds, Inc. by email legal@cyan.com
+ or by snail mail at:
+ Cyan Worlds, Inc.
+ 14617 N Newport Hwy
+ Mead, WA 99021
+
+*==LICENSE==*/
+
+#include "hsFILELock.h"
+
+#include
+#include
+
+#include "hsWindows.h"
+#ifdef _MSC_VER
+# include
+#endif
+#ifndef HS_BUILD_FOR_WIN32
+# include
+#endif
+
+bool hsFILELock::ILock(bool block) const
+{
+ if (fRef == nullptr)
+ return true;
+
+#ifdef HS_BUILD_FOR_WIN32
+ OVERLAPPED o{};
+ DWORD flags = LOCKFILE_EXCLUSIVE_LOCK;
+ if (block)
+ flags |= LOCKFILE_FAIL_IMMEDIATELY;
+
+ // Lock only the first byte of the file - we have
+ // to provide the exact same region to UnlockFileEx(),
+ // and this is a mandatory lock. Totally different than
+ // POSIX file locks, which are advisory and operate on
+ // the whole file.
+ BOOL result = LockFileEx(
+#ifdef _MSC_VER
+ (HANDLE)_get_osfhandle(_fileno(fRef)),
+#else
+ (HANDLE)fileno(fRef),
+#endif // _MSC_VER
+ flags,
+ 0,
+ 1,
+ 0,
+ &o
+ );
+ if (result == 0) {
+ DWORD error = GetLastError();
+ if (!block && error == ERROR_IO_PENDING)
+ return false;
+ // BasicLockable requires an exception to indicate that the
+ // lock was not acquired.
+ throw std::system_error(
+ std::error_code(error, std::system_category()),
+ ST::format(
+ "LockFileEx() failed: {}",
+ hsCOMError(hsLastWin32Error, error)
+ ).to_std_string()
+ );
+ }
+
+#else
+ int op = LOCK_EX;
+ if (!block)
+ op |= LOCK_NB;
+ int result = flock(fileno(fRef), op);
+ if (result == -1) {
+ if (!block && errno == EWOULDBLOCK)
+ return false;
+ // BasicLockable requires an exception to indicate that the
+ // lock was not acquired. Even though EINTR is probably not
+ // an error condition, we should still throw to indicate no
+ // lock was acquired.
+ throw std::system_error(
+ std::error_code(errno, std::system_category()),
+ ST::format(
+ "flock() LOCK_EX failed: {}",
+ strerror(errno)
+ ).to_std_string()
+ );
+ }
+#endif // HS_BUILD_FOR_WIN32
+
+ return true;
+}
+
+void hsFILELock::unlock() const
+{
+ if (fRef == nullptr)
+ return;
+
+#ifdef HS_BUILD_FOR_WIN32
+ OVERLAPPED o{};
+ BOOL result = UnlockFileEx(
+#ifdef _MSC_VER
+ (HANDLE)_get_osfhandle(_fileno(fRef)),
+#else
+ (HANDLE)fileno(fRef),
+#endif // _MSC_VER
+ 0,
+ 1,
+ 0,
+ &o
+ );
+ // BasicLockable doesn't allow exceptions to be
+ // thrown in the unlock method.
+ hsAssert(
+ result,
+ ST::format(
+ "UnlockFileEx() failed: {}",
+ hsCOMError(hsLastWin32Error, GetLastError())
+ ).c_str()
+ );
+
+#else
+ int result = flock(fileno(fRef), LOCK_UN);
+ hsAssert(
+ result == 0,
+ ST::format(
+ "flock() LOCK_UN failed: {}",
+ strerror(errno)
+ ).c_str()
+ );
+#endif // HS_BUILD_FOR_WIN32
+}
diff --git a/Sources/Plasma/CoreLib/hsFILELock.h b/Sources/Plasma/CoreLib/hsFILELock.h
new file mode 100644
index 0000000000..7ee247e5cc
--- /dev/null
+++ b/Sources/Plasma/CoreLib/hsFILELock.h
@@ -0,0 +1,83 @@
+/*==LICENSE==*
+
+CyanWorlds.com Engine - MMOG client, server and tools
+Copyright (C) 2011 Cyan Worlds, Inc.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+Additional permissions under GNU GPL version 3 section 7
+
+If you modify this Program, or any covered work, by linking or
+combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
+NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
+JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
+(or a modified version of those libraries),
+containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
+PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
+JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
+licensors of this Program grant you additional
+permission to convey the resulting work. Corresponding Source for a
+non-source form of such a combination shall include the source code for
+the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
+work.
+
+You can contact Cyan Worlds, Inc. by email legal@cyan.com
+ or by snail mail at:
+ Cyan Worlds, Inc.
+ 14617 N Newport Hwy
+ Mead, WA 99021
+
+*==LICENSE==*/
+
+#ifndef hsFILELock_inc
+#define hsFILELock_inc
+
+#include "HeadSpin.h"
+
+/**
+ * Acquire a platform dependent lock on the underlying file handle.
+ * \remarks On Windows, file locks are obligatory. Any attempts to
+ * read/write from/to the locked region will be denied. The region
+ * that this method locks is an implementation detail. On unix-like
+ * platforms, locks are advisory, and reads/writes from/to the locked
+ * region will succeed. Therefore, file locks should be treated as
+ * advisory at best (they won't stop someone from making a mess), and
+ * mandatory at worst (they can cause reads/writes to fail).
+ * \note This class meets the requirements of `Lockable`.
+ */
+class hsFILELock
+{
+ FILE* fRef;
+
+ bool ILock(bool block) const;
+
+public:
+ hsFILELock() = delete;
+ hsFILELock(const hsFILELock&) = delete;
+ hsFILELock(hsFILELock&& mv)
+ : fRef(mv.fRef)
+ {
+ mv.fRef = nullptr;
+ }
+ hsFILELock(FILE* f) : fRef(f) { }
+
+ void lock() const { ILock(true); }
+
+ [[nodiscard]]
+ bool try_lock() const { return ILock(false); }
+
+ void unlock() const;
+};
+
+#endif
diff --git a/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp b/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp
index 834844b5c8..8df893d7ba 100644
--- a/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp
+++ b/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp
@@ -58,6 +58,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "plStatusLog.h"
#include "plEncryptLogLine.h"
+#include "hsFILELock.h"
#include "plProduct.h"
#include "hsThread.h"
#include "hsTimer.h"
@@ -259,20 +260,17 @@ bool plStatusLogMgr::DumpLogs( const plFileName &newFolderName )
uint32_t plStatusLog::fLoggingOff = false;
plStatusLog::plStatusLog( uint8_t numDisplayLines, const plFileName &filename, uint32_t flags )
- : fFileHandle(), fSema(), fSize(), fForceLog(), fMaxNumLines(numDisplayLines),
+ : fFileHandle(), fSize(), fForceLog(), fMaxNumLines(numDisplayLines),
fDisplayPointer()
{
if (filename.IsValid())
{
fFilename = filename;
- fSema = new hsGlobalSemaphore(1, fFilename.AsString().c_str());
}
else
{
fFilename = "";
flags |= kDontWriteFile;
-
- fSema = new hsGlobalSemaphore(1);
}
fOrigFlags = fFlags = flags;
@@ -366,9 +364,6 @@ void plStatusLog::IFini()
if (fBack != nullptr || fNext != nullptr)
IUnlink();
- if (fSema)
- delete fSema;
-
delete [] fLines;
delete [] fColors;
}
@@ -431,7 +426,8 @@ bool plStatusLog::IAddLine(const ST::string& line, uint32_t color)
return true;
/// Scroll pointers up
- fSema->Wait();
+ hsFILELock fileLock(fFileHandle);
+ hsLockGuard(fileLock);
if (fMaxNumLines > 0)
{
@@ -448,8 +444,6 @@ bool plStatusLog::IAddLine(const ST::string& line, uint32_t color)
bool ret = IPrintLineToFile(line);
- fSema->Signal();
-
return ret;
}
diff --git a/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h b/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h
index 4a42d0d053..1f8bcb6016 100644
--- a/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h
+++ b/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h
@@ -57,7 +57,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#define _plStatusLog_h
#include "HeadSpin.h"
-#include "hsThread.h"
#include "plFileSystem.h"
#include "plLoggable.h"
@@ -88,7 +87,6 @@ class plStatusLog : public plLog
plFileName fFilename;
ST::string* fLines;
uint32_t* fColors;
- hsGlobalSemaphore* fSema;
FILE* fFileHandle;
uint32_t fSize;
bool fForceLog;