1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

Lazy<T>, LazyTLS<T>, thread dtors

This commit is contained in:
Brian Fiete 2022-07-04 10:20:38 -07:00
parent cf269db0eb
commit a27ef9beda
17 changed files with 437 additions and 30 deletions

288
BeefLibs/corlib/src/Lazy.bf Normal file
View file

@ -0,0 +1,288 @@
using System.Threading;
namespace System
{
enum LazyThreadMode
{
None,
Lock,
Lockless
}
class Lazy<T>
{
protected struct Entry
{
public SelfOuter mSingleton;
public T mValue;
}
protected Monitor mMonitor ~ delete _;
protected LazyThreadMode mThreadMode;
protected volatile int mInitId;
protected T mValue;
delegate T() mCreateDlg ~ delete _;
delegate void(T value) mReleaseDlg ~ delete _;
public this()
{
}
public this(LazyThreadMode threadMode)
{
mThreadMode = threadMode;
Init();
}
public this(LazyThreadMode threadMode, delegate T() createDlg = null, delegate void(T value) releaseDlg = null)
{
mThreadMode = threadMode;
mCreateDlg = createDlg;
mReleaseDlg = releaseDlg;
Init();
}
public this(delegate void(T value) releaseDlg) : this()
{
mReleaseDlg = releaseDlg;
}
void Init()
{
switch (mThreadMode)
{
case .None:
case .Lock:
mMonitor = new Monitor();
case .Lockless:
}
}
public ~this()
{
ReleaseValue(mut mValue);
}
protected T DefaultCreateValue()
{
return default;
}
protected T DefaultCreateValue() where T : struct, new
{
return T();
}
protected T DefaultCreateValue() where T : class
{
Runtime.FatalError("No create delegate specified and no public default constructor is available");
}
protected T DefaultCreateValue() where T : class, new
{
return new T();
}
protected virtual T CreateValue()
{
return DefaultCreateValue();
}
protected void DefaultReleaseValue(mut T val)
{
}
protected void DefaultReleaseValue(mut T val) where T : struct, IDisposable
{
val.Dispose();
}
protected void DefaultReleaseValue(mut T val) where T : class
{
delete (Object)val;
}
protected virtual void ReleaseValue(mut T val)
{
DefaultReleaseValue(mut val);
}
public ref T Value
{
get
{
if (mInitId == -1)
return ref mValue;
switch (mThreadMode)
{
case .None:
mValue = CreateValue();
case .Lock:
using (mMonitor.Enter())
{
if (mInitId != -1)
{
mValue = CreateValue();
Interlocked.Fence();
mInitId = -1;
}
}
case .Lockless:
int threadId = Thread.CurrentThreadId;
while (true)
{
int initId = Interlocked.CompareExchange(ref mInitId, 0, threadId);
if (initId == -1)
break;
if (initId == 0)
{
Interlocked.Fence();
mValue = CreateValue();
Interlocked.Fence();
mInitId = -1;
break;
}
}
}
return ref mValue;
}
}
public bool IsValueCreated => mInitId != 0;
public static ref T operator->(Self self) => ref self.[Inline]Value;
public override void ToString(String strBuffer)
{
if (IsValueCreated)
strBuffer.AppendF($"Value: {Value}");
else
strBuffer.AppendF($"No Value");
}
}
class LazyTLS<T>
{
protected struct Entry
{
public SelfOuter mSingleton;
public T mValue;
}
void* mData;
delegate T() mCreateDlg ~ delete _;
delegate void(T value) mReleaseDlg ~ delete _;
public this()
{
InitTLS();
}
public this(delegate T() createDlg = null, delegate void(T value) releaseDlg = null)
{
mCreateDlg = createDlg;
mReleaseDlg = releaseDlg;
InitTLS();
}
void InitTLS()
{
mData = Platform.BfpTLS_Create((ptr) =>
{
var entry = (Entry*)ptr;
if (entry.mSingleton.mReleaseDlg != null)
entry.mSingleton.mReleaseDlg(entry.mValue);
else
entry.mSingleton.ReleaseValue(mut entry.mValue);
delete entry;
});
}
public ~this()
{
Platform.BfpTLS_Release((.)mData);
}
protected T DefaultCreateValue()
{
return default;
}
protected T DefaultCreateValue() where T : struct, new
{
return T();
}
protected T DefaultCreateValue() where T : class
{
Runtime.FatalError("No create delegate specified and no public default constructor is available");
}
protected T DefaultCreateValue() where T : class, new
{
return new T();
}
protected virtual T CreateValue()
{
return DefaultCreateValue();
}
protected void DefaultReleaseValue(mut T val)
{
}
protected void DefaultReleaseValue(mut T val) where T : struct, IDisposable
{
val.Dispose();
}
protected void DefaultReleaseValue(mut T val) where T : class
{
delete (Object)val;
}
protected virtual void ReleaseValue(mut T val)
{
DefaultReleaseValue(mut val);
}
public ref T Value
{
get
{
void* ptr = Platform.BfpTLS_GetValue((.)mData);
if (ptr != null)
return ref ((Entry*)ptr).mValue;
Entry* entry = new Entry();
entry.mSingleton = this;
if (mCreateDlg != null)
entry.mValue = mCreateDlg();
else
entry.mValue = CreateValue();
Platform.BfpTLS_SetValue((.)mData, entry);
return ref entry.mValue;
}
}
public bool IsValueCreated => Platform.BfpTLS_GetValue((.)mData) != null;
public static ref T operator->(Self self) => ref self.[Inline]Value;
public override void ToString(String strBuffer)
{
if (IsValueCreated)
strBuffer.AppendF($"Value: {Value}");
else
strBuffer.AppendF($"No Value");
}
}
}

View file

@ -42,6 +42,7 @@ namespace System
public struct BfpEvent {};
public struct BfpFileWatcher {}
public struct BfpProcess {}
public struct BfpTLS;
public enum BfpSystemResult : int32
{
@ -95,6 +96,18 @@ namespace System
[CallingConvention(.Stdcall), CLink]
public static extern void BfpSystem_GetComputerName(char8* outStr, int32* inOutStrSize, BfpSystemResult* outResult);
[CallingConvention(.Stdcall), CLink]
public static extern int BfpThread_GetCurrentId();
[CallingConvention(.Stdcall), CLink]
public static extern BfpTLS* BfpTLS_Create(function [CallingConvention(.Stdcall)] void(void*) exitProc);
[CallingConvention(.Stdcall), CLink]
public static extern void BfpTLS_Release(BfpTLS* tls);
[CallingConvention(.Stdcall), CLink]
public static extern void BfpTLS_SetValue(BfpTLS* tls, void* value);
[CallingConvention(.Stdcall), CLink]
public static extern void* BfpTLS_GetValue(BfpTLS* tls);
public enum BfpFileWatcherFlags : int32
{
None = 0,

View file

@ -9,7 +9,7 @@ namespace System
[StaticInitPriority(101)]
static class Runtime
{
const int32 cVersion = 9;
const int32 cVersion = 10;
[CRepr, AlwaysInclude]
struct BfDebugMessageData
@ -118,6 +118,7 @@ namespace System
function bool (Object thread) mThread_IsAutoDelete;
function void (Object thread) mThread_AutoDelete;
function int32 (Object thread) mThread_GetMaxStackSize;
function void () mThread_Exiting;
function void () mGC_MarkAllStaticMembers;
function bool () mGC_CallRootCallbacks;
function void () mGC_Shutdown;

View file

@ -20,6 +20,11 @@ namespace System.Threading
private Object mThreadStartArg;
bool mAutoDelete;
static Monitor sMonitor = new .() ~ delete _;
static Event<delegate void()> sOnExit ~ _.Dispose();
Event<delegate void()> mOnExit ~ _.Dispose();
public static Thread sMainThread = new Thread() ~ delete _;
[StaticInitPriority(102)]
@ -62,6 +67,14 @@ namespace System.Threading
return ((Thread)thread).mMaxStackSize;
}
static void Thread_Exiting()
{
using (sMonitor.Enter())
{
sOnExit();
}
}
static void Thread_StartProc(Object threadObj)
{
Thread thread = (Thread)threadObj;
@ -103,6 +116,7 @@ namespace System.Threading
cb.[Friend]mThread_IsAutoDelete = => Thread_IsAutoDelete;
cb.[Friend]mThread_AutoDelete = => Thread_AutoDelete;
cb.[Friend]mThread_GetMaxStackSize = => Thread_GetMaxStackSize;
cb.[Friend]mThread_Exiting = => Thread_Exiting;
}
}
@ -178,6 +192,38 @@ namespace System.Threading
}
}
public void AddExitNotify(delegate void() dlg)
{
using (sMonitor.Enter())
{
mOnExit.Add(dlg);
}
}
public Result<void> RemovedExitNotify(delegate void() dlg, bool delegateDelegate = false)
{
using (sMonitor.Enter())
{
return mOnExit.Remove(dlg, delegateDelegate);
}
}
public static void AddGlobalExitNotify(delegate void() dlg)
{
using (sMonitor.Enter())
{
sOnExit.Add(dlg);
}
}
public static Result<void> RemoveGlobalExitNotify(delegate void() dlg, bool delegateDelegate = false)
{
using (sMonitor.Enter())
{
return sOnExit.Remove(dlg, delegateDelegate);
}
}
extern void ManualThreadInit();
extern void StartInternal();
extern void SetStackStart(void* ptr);
@ -217,6 +263,7 @@ namespace System.Threading
}
}
public static extern void RequestExitNotify();
public extern void Suspend();
public extern void Resume();
@ -316,9 +363,10 @@ namespace System.Threading
}
}
extern int32 GetThreadId();
[CallingConvention(.Cdecl)]
extern int GetThreadId();
public int32 Id
public int Id
{
get
{
@ -326,6 +374,8 @@ namespace System.Threading
}
}
public static int CurrentThreadId => Platform.BfpThread_GetCurrentId();
[CallingConvention(.Cdecl)]
private static extern Thread GetCurrentThreadNative();
@ -337,6 +387,12 @@ namespace System.Threading
public ~this()
{
using (sMonitor.Enter())
{
mOnExit();
sOnExit();
}
// Make sure we're not deleting manually if mAutoDelete is set
Debug.Assert((!mAutoDelete) || (CurrentThread == this));
// Delegate to the unmanaged portion.

View file

@ -3,7 +3,7 @@
#include "BeefySysLib/Common.h"
#include "BeefySysLib/util/String.h"
#define BFRT_VERSION 9
#define BFRT_VERSION 10
#ifdef BFRT_DYNAMIC
#define BFRT_EXPORT __declspec(dllexport)
@ -99,6 +99,7 @@ namespace bf
bool(*Thread_IsAutoDelete)(bf::System::Threading::Thread* thread);
void(*Thread_AutoDelete)(bf::System::Threading::Thread* thread);
int32(*Thread_GetMaxStackSize)(bf::System::Threading::Thread* thread);
void(*Thread_Exiting)();
void(*GC_MarkAllStaticMembers)();
bool(*GC_CallRootCallbacks)();
void(*GC_Shutdown)();

View file

@ -55,6 +55,12 @@ static Beefy::StringT<0> gCmdLineString;
bf::System::Runtime::BfRtCallbacks gBfRtCallbacks;
BfRtFlags gBfRtFlags = (BfRtFlags)0;
#ifdef BF_PLATFORM_WINDOWS
DWORD gBfTLSKey = 0;
#else
pthread_key_t gBfTLSKey = 0;
#endif
static int gTestMethodIdx = -1;
static uint32 gTestStartTick = 0;
static bool gTestBreakOnFailure = false;
@ -287,6 +293,11 @@ static void GetCrashInfo()
}
}
static void NTAPI TlsFreeFunc(void* ptr)
{
gBfRtCallbacks.Thread_Exiting();
}
void bf::System::Runtime::Init(int version, int flags, BfRtCallbacks* callbacks)
{
BfpSystemInitFlags sysInitFlags = BfpSystemInitFlag_InstallCrashCatcher;
@ -343,6 +354,12 @@ void bf::System::Runtime::Init(int version, int flags, BfRtCallbacks* callbacks)
useCmdLineStr++;
}
gCmdLineString = useCmdLineStr;
#ifdef BF_PLATFORM_WINDOWS
gBfTLSKey = FlsAlloc(TlsFreeFunc);
#else
pthread_key_create(&gBfTLSKey, TlsFreeFunc);
#endif
}
void bf::System::Runtime::SetErrorString(char* errorStr)

View file

@ -19,6 +19,12 @@ BF_TLS_DECLSPEC Thread* Thread::sCurrentThread;
static volatile int gLiveThreadCount;
static Beefy::SyncEvent gThreadsDoneEvent;
#ifdef BF_PLATFORM_WINDOWS
extern DWORD gBfTLSKey;
#else
extern pthread_key_t gBfTLSKey;
#endif
bf::System::Threading::Thread* BfGetCurrentThread()
{
#ifdef BF_THREAD_TLS
@ -134,6 +140,7 @@ static void BF_CALLTYPE CStartProc(void* threadParam)
//
{
internalThread->ThreadStopped();
Beefy::AutoCrit autoCrit(internalThread->mCritSect);
if (isAutoDelete)
gBfRtCallbacks.Thread_AutoDelete(thread);
@ -207,15 +214,28 @@ void Thread::StartInternal()
#endif
}
void Thread::RequestExitNotify()
{
// Do we already have implicit exiting notification?
if (BfGetCurrentThread() != NULL)
return;
#ifdef BF_PLATFORM_WINDOWS
FlsSetValue(gBfTLSKey, (void*)&gBfRtCallbacks);
#else
pthread_setspecific(gBfTLSKey, (void*)&gBfRtCallbacks);
#endif
}
void Thread::ThreadStarted()
{
auto internalThread = GetInternalThread();
internalThread->mCritSect.Unlock();
}
int Thread::GetThreadId()
intptr Thread::GetThreadId()
{
return (int)GetInternalThread()->mThreadId;
return GetInternalThread()->mThreadId;
}
void Thread::SetStackStart(void* ptr)

View file

@ -59,7 +59,7 @@ namespace bf
BFRT_EXPORT void SetBackgroundNative(bool isBackground);
BFRT_EXPORT int GetThreadStateNative();
BFRT_EXPORT void InformThreadNameChange(String* name);
BFRT_EXPORT int GetThreadId();
BFRT_EXPORT intptr GetThreadId();
BFRT_EXPORT void Dbg_CreateInternal();
@ -68,6 +68,7 @@ namespace bf
BFRT_EXPORT void Resume();
BFRT_EXPORT void Abort();
BFRT_EXPORT static void RequestExitNotify();
BFRT_EXPORT static void MemoryBarrier();
static Thread* Alloc()

View file

@ -35,7 +35,7 @@ uint32 BfTLSManager::Alloc()
}
}
mAllocatedKeys[idx] = BfpTLS_Create();
mAllocatedKeys[idx] = BfpTLS_Create(NULL);
mAssociatedTLSDatums[idx] = NULL;
return idx;
}

View file

@ -43,7 +43,7 @@ public:
BfTLSManager()
{
sInternalThreadKey = BfpTLS_Create();
sInternalThreadKey = BfpTLS_Create(NULL);
mAssociatedTLSDatums = NULL;
mAllocSize = 0;
mAllocIdx = 1;

View file

@ -223,6 +223,7 @@ enum BfpThreadCreateFlags
};
typedef void (BFP_CALLTYPE *BfpThreadStartProc)(void* threadParam);
typedef void (BFP_CALLTYPE* BfpTLSProc)(void* threadParam);
enum BfpThreadPriority
{
@ -273,8 +274,9 @@ BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Enter(BfpCritSect* critSect);
BFP_EXPORT bool BFP_CALLTYPE BfpCritSect_TryEnter(BfpCritSect* critSect, int waitMS);
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect);
struct BfpTLS;
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create();
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create(BfpTLSProc exitProc);
BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls);
BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value);
BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls);

View file

@ -1528,10 +1528,10 @@ BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
pthread_mutex_unlock(&critSect->mPMutex);
}
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create()
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create(BfpTLSProc exitProc)
{
pthread_key_t key = 0;
pthread_key_create(&key, NULL);
pthread_key_create(&key, exitProc);
return (BfpTLS*)(intptr)key;
}

View file

@ -2530,24 +2530,24 @@ BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
#define BFPTLS_TO_DWORD(val) ((DWORD)(intptr)(val))
struct BfpTLS;
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create()
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create(BfpTLSProc exitProc)
{
return DWORD_TO_BFPTLS(::TlsAlloc());
return DWORD_TO_BFPTLS(::FlsAlloc(exitProc));
}
BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls)
{
::TlsFree(BFPTLS_TO_DWORD(tls));
::FlsFree(BFPTLS_TO_DWORD(tls));
}
BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value)
{
::TlsSetValue(BFPTLS_TO_DWORD(tls), value);
::FlsSetValue(BFPTLS_TO_DWORD(tls), value);
}
BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls)
{
return ::TlsGetValue(BFPTLS_TO_DWORD(tls));
return ::FlsGetValue(BFPTLS_TO_DWORD(tls));
}
BFP_EXPORT BfpEvent* BFP_CALLTYPE BfpEvent_Create(BfpEventFlags flags)

View file

@ -38,7 +38,7 @@ namespace System.Diagnostics
}
}
static Result<ProfileId> StartSampling(int32 threadId, StringView profileDesc)
static Result<ProfileId> StartSampling(int threadId, StringView profileDesc)
{
//int32 curId = Interlocked.Increment(ref gProfileId);
int32 curId = gProfileId++;

View file

@ -95,7 +95,7 @@ namespace System
extension Result<T> where T : IDisposable
{
public void Dispose()
public new void Dispose()
{
if (this case .Ok(var val))
val.Dispose();
@ -199,7 +199,7 @@ namespace System
extension Result<T, TErr> where T : IDisposable
{
public void Dispose()
public new void Dispose()
{
if (this case .Ok(var val))
val.Dispose();
@ -208,7 +208,7 @@ namespace System
extension Result<T, TErr> where TErr : IDisposable
{
public void Dispose()
public new void Dispose()
{
if (this case .Err(var err))
err.Dispose();
@ -217,7 +217,7 @@ namespace System
extension Result<T, TErr> where T : IDisposable where TErr : IDisposable
{
public void Dispose()
public new void Dispose()
{
if (this case .Ok(var val))
val.Dispose();

View file

@ -7,7 +7,7 @@ namespace System
[StaticInitPriority(100)]
class Runtime
{
const int32 cVersion = 9;
const int32 cVersion = 10;
[CRepr, AlwaysInclude]
struct BfDebugMessageData
@ -116,6 +116,7 @@ namespace System
function bool (Object thread) mThread_IsAutoDelete;
function void (Object thread) mThread_AutoDelete;
function int32 (Object thread) mThread_GetMaxStackSize;
function void () mThread_Exiting;
function void () mGC_MarkAllStaticMembers;
function bool () mGC_CallRootCallbacks;
function void () mGC_Shutdown;

View file

@ -78,6 +78,11 @@ namespace System.Threading
}
}
static void Thread_Exiting()
{
}
public static this()
{
var cb = ref Runtime.BfRtCallbacks.[Friend]sCallbacks;
@ -90,6 +95,7 @@ namespace System.Threading
cb.[Friend]mThread_IsAutoDelete = => Thread_IsAutoDelete;
cb.[Friend]mThread_AutoDelete = => Thread_AutoDelete;
cb.[Friend]mThread_GetMaxStackSize = => Thread_GetMaxStackSize;
cb.[Friend]mThread_Exiting = => Thread_Exiting;
}
}
@ -280,9 +286,10 @@ namespace System.Threading
}
}
extern int32 GetThreadId();
[CallingConvention(.Cdecl)]
extern int GetThreadId();
public int32 Id
public int Id
{
get
{