aboutsummaryrefslogtreecommitdiff
path: root/zencore/include
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2021-09-21 11:06:13 +0200
committerMartin Ridgers <[email protected]>2021-09-21 11:06:13 +0200
commit68c951e0f440ffd483795dced737e88152c1a581 (patch)
tree5c0910ca2a85b45fb05dba3ce457b7d156213894 /zencore/include
parentMerge main into linux-mac (diff)
parentTrigger storage scrubbing pass at startup (diff)
downloadzen-68c951e0f440ffd483795dced737e88152c1a581.tar.xz
zen-68c951e0f440ffd483795dced737e88152c1a581.zip
Merged main into linux-mac
Diffstat (limited to 'zencore/include')
-rw-r--r--zencore/include/zencore/except.h53
-rw-r--r--zencore/include/zencore/logging.h7
-rw-r--r--zencore/include/zencore/mpscqueue.h109
-rw-r--r--zencore/include/zencore/session.h3
-rw-r--r--zencore/include/zencore/snapshot_manifest.h57
-rw-r--r--zencore/include/zencore/string.h39
-rw-r--r--zencore/include/zencore/testing.h9
-rw-r--r--zencore/include/zencore/testutils.h31
-rw-r--r--zencore/include/zencore/thread.h33
-rw-r--r--zencore/include/zencore/uid.h2
-rw-r--r--zencore/include/zencore/zencore.h94
11 files changed, 302 insertions, 135 deletions
diff --git a/zencore/include/zencore/except.h b/zencore/include/zencore/except.h
index f0e04a795..1dc6209d6 100644
--- a/zencore/include/zencore/except.h
+++ b/zencore/include/zencore/except.h
@@ -15,63 +15,20 @@
namespace zen {
#if ZEN_PLATFORM_WINDOWS
-class WindowsException : public std::exception
-{
-public:
- WindowsException(std::string_view Message)
- {
- m_hResult = HRESULT_FROM_WIN32(GetLastError());
- m_Message = Message;
- }
-
- WindowsException(HRESULT hRes, std::string_view Message)
- {
- m_hResult = hRes;
- m_Message = Message;
- }
-
- WindowsException(HRESULT hRes, const char* Message, const char* Detail)
- {
- m_hResult = hRes;
-
- ExtendableStringBuilder<128> msg;
- msg.Append(Message);
- msg.Append(" (detail: '");
- msg.Append(Detail);
- msg.Append("')");
-
- m_Message = msg.c_str();
- }
-
- virtual const char* what() const override { return m_Message.c_str(); }
-
-private:
- std::string m_Message;
- HRESULT m_hResult;
-};
-
-ZENCORE_API void ThrowSystemException(HRESULT hRes, std::string_view Message);
+ZENCORE_API void ThrowSystemException [[noreturn]] (HRESULT hRes, std::string_view Message);
#endif // ZEN_PLATFORM_WINDOWS
-ZENCORE_API void ThrowLastError(std::string_view Message);
+ZENCORE_API void ThrowLastError [[noreturn]] (std::string_view Message);
#if __cpp_lib_source_location
-ZENCORE_API void ThrowLastError(std::string_view Message, const std::source_location& Location);
+ZENCORE_API void ThrowLastError [[noreturn]] (std::string_view Message, const std::source_location& Location);
#endif
+ZENCORE_API void ThrowSystemError [[noreturn]] (uint32_t ErrorCode, std::string_view Message);
+
ZENCORE_API std::string GetLastErrorAsString();
ZENCORE_API std::string GetErrorAsString(uint32_t ErrorCode);
-inline void
-ThrowSystemException(const char* Message)
-{
-#if ZEN_PLATFORM_WINDOWS
- throw WindowsException(Message);
-#else
- ThrowLastError(Message);
-#endif
-}
-
inline int32_t
GetLastError()
{
diff --git a/zencore/include/zencore/logging.h b/zencore/include/zencore/logging.h
index 412a39415..221f5f358 100644
--- a/zencore/include/zencore/logging.h
+++ b/zencore/include/zencore/logging.h
@@ -80,3 +80,10 @@ using zen::Log;
using namespace std::literals; \
Log().critical(fmtstr##sv, ##__VA_ARGS__);\
} while (false)
+
+#define ZEN_CONSOLE(fmtstr, ...) \
+ do \
+ { \
+ using namespace std::literals; \
+ ConsoleLog().info(fmtstr##sv, __VA_ARGS__); \
+ } while (false)
diff --git a/zencore/include/zencore/mpscqueue.h b/zencore/include/zencore/mpscqueue.h
new file mode 100644
index 000000000..bb558bb5a
--- /dev/null
+++ b/zencore/include/zencore/mpscqueue.h
@@ -0,0 +1,109 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <atomic>
+#include <new>
+#include <optional>
+
+#ifdef __cpp_lib_hardware_interference_size
+using std::hardware_constructive_interference_size;
+using std::hardware_destructive_interference_size;
+#else
+// 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ...
+constexpr std::size_t hardware_constructive_interference_size = 64;
+constexpr std::size_t hardware_destructive_interference_size = 64;
+#endif
+
+namespace zen {
+
+/** An untyped array of data with compile-time alignment and size derived from another type. */
+template<typename ElementType>
+struct TypeCompatibleStorage
+{
+ ElementType* Data() { return (ElementType*)this; }
+ const ElementType* Data() const { return (const ElementType*)this; }
+
+ char alignas(ElementType) DataMember;
+};
+
+/** Fast multi-producer/single-consumer unbounded concurrent queue.
+
+ Based on http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue
+ */
+
+template<typename T>
+class MpscQueue final
+{
+public:
+ using ElementType = T;
+
+ MpscQueue()
+ {
+ Node* Sentinel = new Node;
+ Head.store(Sentinel, std::memory_order_relaxed);
+ Tail = Sentinel;
+ }
+
+ ~MpscQueue()
+ {
+ Node* Next = Tail->Next.load(std::memory_order_relaxed);
+
+ // sentinel's value is already destroyed
+ delete Tail;
+
+ while (Next != nullptr)
+ {
+ Tail = Next;
+ Next = Tail->Next.load(std::memory_order_relaxed);
+
+ std::destroy_at((ElementType*)&Tail->Value);
+ delete Tail;
+ }
+ }
+
+ template<typename... ArgTypes>
+ void Enqueue(ArgTypes&&... Args)
+ {
+ Node* New = new Node;
+ new (&New->Value) ElementType(std::forward<ArgTypes>(Args)...);
+
+ Node* Prev = Head.exchange(New, std::memory_order_acq_rel);
+ Prev->Next.store(New, std::memory_order_release);
+ }
+
+ std::optional<ElementType> Dequeue()
+ {
+ Node* Next = Tail->Next.load(std::memory_order_acquire);
+
+ if (Next == nullptr)
+ {
+ return {};
+ }
+
+ ElementType* ValuePtr = (ElementType*)&Next->Value;
+ std::optional<ElementType> Res{std::move(*ValuePtr)};
+ std::destroy_at(ValuePtr);
+
+ delete Tail; // current sentinel
+
+ Tail = Next; // new sentinel
+ return Res;
+ }
+
+private:
+ struct Node
+ {
+ std::atomic<Node*> Next{nullptr};
+ TypeCompatibleStorage<ElementType> Value;
+ };
+
+private:
+ std::atomic<Node*> Head; // accessed only by producers
+ alignas(hardware_constructive_interference_size)
+ Node* Tail; // accessed only by consumer, hence should be on a different cache line than `Head`
+};
+
+void mpscqueue_forcelink();
+
+} // namespace zen
diff --git a/zencore/include/zencore/session.h b/zencore/include/zencore/session.h
index 2da41b2c8..dd90197bf 100644
--- a/zencore/include/zencore/session.h
+++ b/zencore/include/zencore/session.h
@@ -8,6 +8,7 @@ namespace zen {
struct Oid;
-ZENCORE_API Oid GetSessionId();
+ZENCORE_API [[nodiscard]] Oid GetSessionId();
+ZENCORE_API [[nodiscard]] std::string_view GetSessionIdString();
} // namespace zen
diff --git a/zencore/include/zencore/snapshot_manifest.h b/zencore/include/zencore/snapshot_manifest.h
deleted file mode 100644
index 95e64773a..000000000
--- a/zencore/include/zencore/snapshot_manifest.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#include <zencore/iohash.h>
-#include <zencore/zencore.h>
-
-#include <filesystem>
-#include <functional>
-#include <string>
-#include <vector>
-
-namespace zen {
-
-struct LeafNode
-{
- uint64_t FileSize = 0;
- uint64_t FileModifiedTime = 0;
- zen::IoHash ChunkHash = zen::IoHash::Zero;
- std::wstring Name;
-};
-
-struct TreeNode
-{
- std::vector<TreeNode> Children;
- std::vector<LeafNode> Leaves;
- std::wstring Name;
- zen::BLAKE3 ChunkHash = zen::BLAKE3::Zero;
-
- ZENCORE_API void VisitModifyFiles(std::function<void(LeafNode& node)> func);
- ZENCORE_API void VisitFiles(std::function<void(const LeafNode& node)> func);
- ZENCORE_API void Finalize();
-};
-
-struct SnapshotManifest
-{
- std::string Id;
- TreeNode Root;
- zen::BLAKE3 ChunkHash = zen::BLAKE3::Zero;
-
- ZENCORE_API void finalize();
-};
-
-class InStream;
-class OutStream;
-
-ZENCORE_API void ReadManifest(SnapshotManifest& Manifest, InStream& FromStream);
-ZENCORE_API void WriteManifest(const SnapshotManifest& Manifest, OutStream& ToStream);
-ZENCORE_API void PrintManifest(const SnapshotManifest& Manifest, OutStream& ToStream);
-
-// Translate a user-provided manifest specification into a file path.
-// Supports hashtag syntax to implicitly refer to user documents zenfs folder
-ZENCORE_API std::filesystem::path ManifestSpecToPath(const char* ManifestSpec);
-
-void snapshotmanifest_forcelink();
-
-} // namespace zen
diff --git a/zencore/include/zencore/string.h b/zencore/include/zencore/string.h
index 2b5f20f86..bb9b1c896 100644
--- a/zencore/include/zencore/string.h
+++ b/zencore/include/zencore/string.h
@@ -622,6 +622,45 @@ ToLower(const std::string_view& InString)
//////////////////////////////////////////////////////////////////////////
+template<typename Fn>
+uint32_t
+ForEachStrTok(const std::string_view& Str, char Delim, Fn&& Func)
+{
+ auto It = Str.begin();
+ auto End = Str.end();
+ uint32_t Count = 0;
+
+ while (It != End)
+ {
+ if (*It == Delim)
+ {
+ It++;
+ continue;
+ }
+
+ std::string_view Remaining{It, End};
+ size_t Idx = Remaining.find(Delim, 0);
+
+ if (Idx == std::string_view::npos)
+ {
+ Idx = Remaining.size();
+ }
+
+ Count++;
+ std::string_view Token{It, It + Idx};
+ if (!Func(Token))
+ {
+ break;
+ }
+
+ It = It + Idx;
+ }
+
+ return Count;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
void string_forcelink(); // internal
} // namespace zen
diff --git a/zencore/include/zencore/testing.h b/zencore/include/zencore/testing.h
new file mode 100644
index 000000000..80aebc26e
--- /dev/null
+++ b/zencore/include/zencore/testing.h
@@ -0,0 +1,9 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zencore/zencore.h>
+
+#if ZEN_WITH_TESTS
+# include <doctest/doctest.h>
+#endif
diff --git a/zencore/include/zencore/testutils.h b/zencore/include/zencore/testutils.h
new file mode 100644
index 000000000..72d985d5c
--- /dev/null
+++ b/zencore/include/zencore/testutils.h
@@ -0,0 +1,31 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <filesystem>
+
+namespace zen {
+
+std::filesystem::path CreateTemporaryDirectory();
+
+class ScopedTemporaryDirectory
+{
+public:
+ ScopedTemporaryDirectory();
+ ~ScopedTemporaryDirectory();
+
+ std::filesystem::path& Path() { return m_RootPath; }
+
+private:
+ std::filesystem::path m_RootPath;
+};
+
+struct ScopedCurrentDirectoryChange
+{
+ std::filesystem::path OldPath{std::filesystem::current_path()};
+
+ ScopedCurrentDirectoryChange() { std::filesystem::current_path(CreateTemporaryDirectory()); }
+ ~ScopedCurrentDirectoryChange() { std::filesystem::current_path(OldPath); }
+};
+
+} // namespace zen
diff --git a/zencore/include/zencore/thread.h b/zencore/include/zencore/thread.h
index b18da6031..7889682cd 100644
--- a/zencore/include/zencore/thread.h
+++ b/zencore/include/zencore/thread.h
@@ -4,9 +4,9 @@
#include "zencore.h"
-#if !ZEN_PLATFORM_WINDOWS
-# include <shared_mutex>
-#endif
+#include <shared_mutex>
+
+#include <vector>
namespace zen {
@@ -64,11 +64,7 @@ public:
};
private:
-#if ZEN_PLATFORM_WINDOWS
- void* m_Srw = nullptr;
-#else
std::shared_mutex m_Mutex;
-#endif
};
/** Basic abstraction of a simple event synchronization mechanism (aka 'binary semaphore')
@@ -93,6 +89,7 @@ public:
ZENCORE_API void Set();
ZENCORE_API void Reset();
ZENCORE_API bool Wait(int TimeoutMs = -1);
+ ZENCORE_API void Close();
protected:
explicit Event(void* EventHandle) : m_EventHandle(EventHandle) {}
@@ -150,6 +147,28 @@ private:
int m_Pid = 0;
};
+/** Process monitor - monitors a list of running processes via polling
+
+ Intended to be used to monitor a set of "sponsor" processes, where
+ we need to determine when none of them remain alive
+
+ */
+
+class ProcessMonitor
+{
+public:
+ ProcessMonitor();
+ ~ProcessMonitor();
+
+ ZENCORE_API bool IsRunning();
+ ZENCORE_API void AddPid(int Pid);
+ ZENCORE_API bool IsActive() const;
+
+private:
+ mutable RwLock m_Lock;
+ std::vector<void*> m_ProcessHandles;
+};
+
ZENCORE_API bool IsProcessRunning(int pid);
ZENCORE_API int GetCurrentProcessId();
diff --git a/zencore/include/zencore/uid.h b/zencore/include/zencore/uid.h
index 2730b1415..f4e9ab65a 100644
--- a/zencore/include/zencore/uid.h
+++ b/zencore/include/zencore/uid.h
@@ -60,6 +60,8 @@ struct Oid
const Oid& Generate();
[[nodiscard]] static Oid FromHexString(const std::string_view String);
StringBuilderBase& ToString(StringBuilderBase& OutString) const;
+ void ToString(char OutString[StringLength]);
+ [[nodiscard]] static Oid FromMemory(const void* Ptr);
auto operator<=>(const Oid& rhs) const = default;
[[nodiscard]] inline operator bool() const { return *this == Zero; }
diff --git a/zencore/include/zencore/zencore.h b/zencore/include/zencore/zencore.h
index 54df7e85e..f6093cb96 100644
--- a/zencore/include/zencore/zencore.h
+++ b/zencore/include/zencore/zencore.h
@@ -3,9 +3,13 @@
#pragma once
#include <cinttypes>
-#include <exception>
+#include <stdexcept>
#include <string>
+#ifndef ZEN_WITH_TESTS
+# define ZEN_WITH_TESTS 1
+#endif
+
//////////////////////////////////////////////////////////////////////////
// Platform
//
@@ -56,6 +60,10 @@
# endif
#endif
+#if ZEN_COMPILER_MSC
+# pragma warning(disable : 4324) // warning C4324: '<type>': structure was padded due to alignment specifier
+#endif
+
//////////////////////////////////////////////////////////////////////////
// Architecture
//
@@ -90,37 +98,69 @@
// Assert
//
+#if ZEN_PLATFORM_WINDOWS
+// Tells the compiler to put the decorated function in a certain section (aka. segment) of the executable.
+# define ZEN_CODE_SECTION(Name) __declspec(code_seg(Name))
+# define ZEN_FORCENOINLINE __declspec(noinline) /* Force code to NOT be inline */
+#else
+# define ZEN_CODE_SECTION(Name)
+# define ZEN_FORCENOINLINE
+#endif
+
+#if ZEN_ARCH_ARM64
+// On ARM we can't do this because the executable will require jumps larger
+// than the branch instruction can handle. Clang will only generate
+// the trampolines in the .text segment of the binary. If the uedbg segment
+// is present it will generate code that it cannot link.
+# define ZEN_DEBUG_SECTION
+#else
+// We'll put all assert implementation code into a separate section in the linked
+// executable. This code should never execute so using a separate section keeps
+// it well off the hot path and hopefully out of the instruction cache. It also
+// facilitates reasoning about the makeup of a compiled/linked binary.
+# define ZEN_DEBUG_SECTION ZEN_CODE_SECTION(".zcold")
+#endif // DO_CHECK || DO_GUARD_SLOW
+
namespace zen {
-class AssertException : public std::exception
+class AssertException : public std::logic_error
{
public:
- AssertException(const char* Msg);
- ~AssertException();
-
- [[nodiscard]] virtual char const* what() const noexcept override { return m_Msg.c_str(); }
-
-private:
- std::string m_Msg;
+ AssertException(const char* Msg) : std::logic_error(Msg) {}
};
} // namespace zen
-#define ZEN_ASSERT(x, ...) \
- do \
- { \
- if (x) \
- break; \
- throw ::zen::AssertException{#x}; \
+template<typename RetType = void, class InnerType, typename... ArgTypes>
+RetType ZEN_FORCENOINLINE ZEN_DEBUG_SECTION
+DispatchAssert(InnerType&& Inner, ArgTypes const&... Args)
+{
+ return Inner(Args...);
+}
+
+#define ZEN_ASSERT(x, ...) \
+ do \
+ { \
+ if (x) [[unlikely]] \
+ break; \
+ struct Impl \
+ { \
+ static void ZEN_FORCENOINLINE ZEN_DEBUG_SECTION ExecThrow [[noreturn]] () { throw ::zen::AssertException{#x}; } \
+ }; \
+ Impl::ExecThrow(); \
} while (false)
#ifndef NDEBUG
-# define ZEN_ASSERT_SLOW(x, ...) \
- do \
- { \
- if (x) \
- break; \
- throw ::zen::AssertException{#x}; \
+# define ZEN_ASSERT_SLOW(x, ...) \
+ do \
+ { \
+ if (x) [[unlikely]] \
+ break; \
+ struct Impl \
+ { \
+ static void ZEN_FORCENOINLINE ZEN_DEBUG_SECTION ExecThrow [[noreturn]] () { throw ::zen::AssertException{#x}; } \
+ }; \
+ Impl::ExecThrow(); \
} while (false)
#else
# define ZEN_ASSERT_SLOW(x, ...)
@@ -148,14 +188,24 @@ char (&ZenArrayCountHelper(const T (&)[N]))[N + 1];
#define ZEN_UNUSED(...) ((void)__VA_ARGS__)
#define ZEN_NOT_IMPLEMENTED(...) ZEN_ASSERT(false, __VA_ARGS__)
-#define ZENCORE_API // Placeholder to allow DLL configs in the future
+#define ZENCORE_API // Placeholder to allow DLL configs in the future (maybe)
+
+namespace zen {
ZENCORE_API bool IsPointerToStack(const void* ptr); // Query if pointer is within the stack of the currently executing thread
ZENCORE_API bool IsApplicationExitRequested();
ZENCORE_API void RequestApplicationExit(int ExitCode);
+ZENCORE_API bool IsDebuggerPresent();
+ZENCORE_API bool IsInteractiveSession();
ZENCORE_API void zencore_forcelinktests();
+} // namespace zen
+
+#ifndef ZEN_USE_MIMALLOC
+# define ZEN_USE_MIMALLOC 1
+#endif
+
//////////////////////////////////////////////////////////////////////////
#if ZEN_COMPILER_MSC