COM reference leak when procedure trapped

COM reference leak when procedure trapped

Postby X512 » Tue Feb 02, 2021 12:35 pm

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.
X512
 
Posts: 64
Joined: Sat Feb 07, 2015 2:51 pm

Re: COM reference leak when procedure trapped

Postby luowy » Thu Feb 04, 2021 11:55 am

understand, but how to do?
luowy
 
Posts: 74
Joined: Thu Dec 17, 2015 1:32 pm

Re: COM reference leak when procedure trapped

Postby X512 » Sat Feb 06, 2021 8:06 am

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.
X512
 
Posts: 64
Joined: Sat Feb 07, 2015 2:51 pm

Re: COM reference leak when procedure trapped

Postby luowy » Sat Feb 27, 2021 3:16 am

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


Return to Bug

Who is online

Users browsing this forum: No registered users and 1 guest

cron