-
assigned issue to
- changed milestone to 1.3
- changed component to Core.Container
- marked as enhancement
Destroying instance of object resolved by container with SingletonPerThread lifetime
it will be not bad if in TSingletonPerThreadLifetimeManager class add code, that destroy instanse of objects created per tread when thread is terminating
we have proposal to add subscibe for event OnTerminate of thread in function TSingletonPerThreadLifetimeManager.Resolve
procedure TSingletonPerThreadLifetimeManager.OnTerminate(sender: TObject);
var
pThread : TThread;
threadID: THandle;
holder: TFunc<TValue>;
begin
pThread := TThread(sender);
threadID := pThread.ThreadID;
if fInstances.TryGetValue(threadID, holder) then
begin
DoBeforeDestruction(holder);
fInstances.Remove(threadID);
end;
end;
function TSingletonPerThreadLifetimeManager.Resolve(
const context: ICreationContext; const model: TComponentModel): TValue;
var
threadID: THandle;
instance: TValue;
holder: TFunc<TValue>;
begin
threadID := TThread.CurrentThread.ThreadID;
MonitorEnter(Self);
try
if not fInstances.TryGetValue(threadID, holder) then
begin
instance := model.ComponentActivator.CreateInstance(context);
holder := CreateHolder(instance, model.RefCounting);
fInstances.AddOrSetValue(threadID, holder);
TThread.CurrentThread.OnTerminate := OnTerminate;
DoAfterConstruction(holder);
end;
finally
MonitorExit(Self);
end;
Result := holder;
end;
we think that this proposal don't break anything
Comments (7)
-
repo owner -
repo owner - changed title to Destroying instance of object resolved by container with SingletonPerThread lifetime
-
repo owner - edited description
- changed status to open
-
repo owner That approach fails as soon as you have more than one instances as singletonPerThread. Also not every thread does OnTerminate (afaik threads that are not created by the RTL for example).
To solve this issue the singleton tracking inside the lifetime manager should be done differently - I just don't know how exactly. Maybe using weak reference might help as that allows handing out the same instance as long as it is alive but allow the instance to be destroyed by the ref counting mechanism at the end of the unit of work inside a thread.
-
repo owner - changed milestone to 2.0
-
repo owner - changed milestone to 2.1
-
Account Deactivated I have a couple of suggestions for detecting the end of the thread:
- Use FiberLocalStorage FlsAlloc(), passing a callback function that gets called when the fiber exits (well, thread exits. Every thread has at least one fiber), making sure to call FlsSetValue with a non-nil value in the thread to make sure the callback gets called. This is not supported before Windows Vista… Does that matter?
- Hook SystemThreadEndProc.
A working example showing both techniques:
program ThreadExitSpike; {$APPTYPE CONSOLE} {$R *.res} uses WinApi.Windows, System.Classes, System.SysUtils; var MNextSystemThreadEndProc: TSystemThreadEndProc; procedure NewSystemThreadEndProc(ExitCode: Integer); begin WriteLn(Format('Thread Exit. Thread %d', [GetCurrentThreadId])); if Assigned(MNextSystemThreadEndProc) then MNextSystemThreadEndProc(ExitCode); end; procedure FiberExit(AFLSData: pointer); stdcall; begin WriteLn(Format('Fiber Exit. Thread %d', [GetCurrentThreadId])); end; var LThread: TThread; LFlsIndex: cardinal; LProc: TProc; begin MNextSystemThreadEndProc := SystemThreadEndProc; SystemThreadEndProc := NewSystemThreadEndProc; WriteLn(Format('Main thread %d', [GetCurrentThreadID])); WriteLn('Setting up FLS'); LFlsIndex := FlsAlloc(@FiberExit); if LFlsIndex = $FFFFFFFF then begin WriteLn('FLS allocation failed'); Exit; end; LProc := procedure () const CDummy = 1; begin WriteLn(Format('Setting FLS value. Thread %d', [GetCurrentThreadId])); FlsSetValue(LFlsIndex, pointer(CDummy)); end; WriteLn('Starting thread 1'); LThread := TThread.CreateAnonymousThread(LProc); LThread.Start; WriteLn('Sleeping for a bit'); Sleep(40); WriteLn('Starting thread 2'); LThread := TThread.CreateAnonymousThread(LProc); LThread.Start; WriteLn('Sleeping for a bit'); Sleep(40); WriteLn('Exiting'); end.
Output:
Main thread 9604 Setting up FLS Starting thread 1 Sleeping for a bit Setting FLS value. Thread 29840 Thread Exit. Thread 29840 Fiber Exit. Thread 29840 Starting thread 2 Sleeping for a bit Setting FLS value. Thread 30512 Thread Exit. Thread 30512 Fiber Exit. Thread 30512 Exiting
- Log in to comment