COM reference leak when procedure trapped

Post Reply
X512
Posts: 72
Joined: Sat Feb 07, 2015 2:51 pm

COM reference leak when procedure trapped

Post 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.
luowy
Posts: 87
Joined: Thu Dec 17, 2015 1:32 pm

Re: COM reference leak when procedure trapped

Post by luowy »

understand, but how to do?
X512
Posts: 72
Joined: Sat Feb 07, 2015 2:51 pm

Re: COM reference leak when procedure trapped

Post 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.
luowy
Posts: 87
Joined: Thu Dec 17, 2015 1:32 pm

Re: COM reference leak when procedure trapped

Post 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!
Attachments
blackbox-1.7.3-a1.1133-COM.zip
(6.7 MiB) Downloaded 329 times
Post Reply