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;