Page 1 of 1
COM reference leak when procedure trapped
Posted: Tue Feb 02, 2021 12:35 pm
by X512
Test code:
Code: Select all
MODULE A;
IMPORT COM, Log := StdLog;
TYPE
Object = POINTER TO RECORD (COM.IUnknown)
END;
PROCEDURE (o: Object) RELEASE;
BEGIN
Log.String("Object.RELEASE"); Log.Ln;
END RELEASE;
PROCEDURE (o: Object) FINALIZE;
BEGIN
Log.String("Object.FINALIZE"); Log.Ln;
END FINALIZE;
PROCEDURE NewObject (): Object;
VAR o: Object;
BEGIN
NEW(o);
RETURN o;
END NewObject;
PROCEDURE Do*;
VAR obj: COM.IUnknown;
BEGIN
obj := NewObject();
HALT(0);
END Do;
END A.
A.Do
Kernel.Collect
DevDecoder.Decode A
When calling A.Do, obj.Release is not called and reference become leaked causing memory leak. Leaked objects can be confirmad in COM -> Show Interfaces menu. Leaked objects can't be collected by garbage collector.
SEH handler that call IUnknown.Release should be installed to fix issue. SEH handler code generator is already present and used for [guarded] procedures.
Note that Blackbox compiler implements RAII for IUnknown pointers, IUnknown.Release method is called when procedure returns.
Re: COM reference leak when procedure trapped
Posted: Thu Feb 04, 2021 11:55 am
by luowy
understand, but how to do?
Re: COM reference leak when procedure trapped
Posted: Sat Feb 06, 2021 8:06 am
by X512
Something like this should be generated by compiler to solve issue (code is functional):
Code: Select all
MODULE A;
IMPORT S := SYSTEM, W := WinApi, COM, Log := StdLog;
CONST
ExceptionContinueExecution = 0;
ExceptionContinueSearch = 1;
ExceptionNestedException = 2;
ExceptionCollidedUnwind = 3;
EXCEPTION_UNWINDING = {1};
EXCEPTION_EXIT_UNWIND = {2};
EXCEPTION_STACK_INVALID = {3};
EXCEPTION_NESTED_CALL = {4};
EXCEPTION_TARGET_UNWIND = {5};
EXCEPTION_COLLIDED_UNWIND = {6};
TYPE
ExcpFramePtr = POINTER TO ExcpFrame;
ExcpFrame = EXTENSIBLE RECORD [untagged]
link: ExcpFramePtr;
handler: PROCEDURE [ccall] (
excpRec: W.PtrEXCEPTION_RECORD;
estFrame: ExcpFramePtr;
context: W.PtrCONTEXT;
dispCont: INTEGER
): INTEGER;
END;
ReleaseHandlerPtr = POINTER TO ReleaseHandler;
ReleaseHandler = RECORD (ExcpFrame)
ptr: INTEGER;
END;
Object = POINTER TO RECORD (COM.IUnknown)
END;
PROCEDURE [code] InstallExcp (VAR e: ExcpFrame) 64H, 8BH, 0DH, 0, 0, 0, 0, 89H, 8, 64H, 0A3H, 0, 0, 0, 0;
PROCEDURE [code] RemoveExcp (VAR e: ExcpFrame) 8BH, 0, 64H, 0A3H, 0, 0, 0, 0;
PROCEDURE [ccall] ExcpHandler (
excpRec: W.PtrEXCEPTION_RECORD;
estFrame: ExcpFramePtr;
context: W.PtrCONTEXT;
dispCont: INTEGER
): INTEGER;
VAR unk: COM.IUnknown;
BEGIN
Log.String("ExcpHandler(");
Log.Set(excpRec.ExceptionFlags);
Log.String(")"); Log.Ln;
IF excpRec.ExceptionFlags * (EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND) # {} THEN
(* cleanup phase, called from W.RtlUnwind *)
(* call ptr.Release *)
S.PUT(S.ADR(unk), estFrame(ReleaseHandlerPtr).ptr); unk := NIL;
ELSE
(* final handler search phase *)
END;
RETURN ExceptionContinueSearch;
END ExcpHandler;
PROCEDURE (o: Object) RELEASE;
BEGIN
Log.String("Object.RELEASE"); Log.Ln;
END RELEASE;
PROCEDURE (o: Object) FINALIZE;
BEGIN
Log.String("Object.FINALIZE"); Log.Ln;
END FINALIZE;
PROCEDURE NewObject (): Object;
VAR o: Object;
BEGIN
NEW(o);
RETURN o;
END NewObject;
PROCEDURE Do*;
VAR excp: ReleaseHandler; obj: COM.IUnknown;
BEGIN
excp.handler := ExcpHandler;
excp.ptr := 0;
InstallExcp(excp);
obj := NewObject();
excp.ptr := S.VAL(INTEGER, obj);
HALT(0);
RemoveExcp(excp);
END Do;
END A.
A.Do
Kernel.Collect
DevDecoder.Decode A
Compiler already generate SEH handler install code for [guarded] procedures, handler is Kernel.InterfaceTrapHandler. It can be reused and new SEH handler can be created for calling IUnknown.Release for COM pointer in local variables. COM pointer list should be generated by compiler and passed to SEH handler.
Re: COM reference leak when procedure trapped
Posted: Sat Feb 27, 2021 3:16 am
by luowy
thanks X512!
I made a fixup, three files has changed: DevCPC486, DevCPV486, Kernel;
For easy testing, I build the whole FW(base on the last distribution of community), TestCom is a test file;
Feedback is welcome!