[fpc-devel] Reference counting interface objects

Adriaan van Os fpc at microbizz.nl
Thu Oct 16 16:43:00 CEST 2014


Jonas Maebe wrote:

> No, it won't:
> 1) if the external code expects it to be "out" and you declare it as 
> "var": the caller will decrease the reference count and the ppEvent that 
> you get will have as initial value "nil". You can just assign to it like 
> normal.
> 2) if the external code expects it to be "var" and you declare it as 
> "out": the caller will not do anything and hence the ppEvent will not be 
> nil. However, assignments to the "out" parameter still cause its 
> reference count to be decreased. After all, you can assign multiple 
> times to an "out" parameter, so the compiler cannot assume it's already 
> nil. The only problem that could happen here is if you would explicitly 
> write your code in a way that behaves differently depending on whether 
> the initial value is nil (e.g., assigning something to it only in one 
> branch of an if-statement, and then later checking whether it's "still" 
> nil to determine whether you have to assign something else to it).

Sorry, but the above explanation flabberghasts me.

case 1.

MF binding declaration

          function GetEvent
             (     dwFlags                   : DWORD;
               out ppEvent                   : IMFMediaEvent): HResult; stdcall;

Pascal code is the caller and Windows MF is the callee.

         theResult                     := theSession.GetEvent
           ( 0, theEvent);

  The compiler sees the out parameter and (as you explained)

> * OUT: the reference count of the value that's passed in is decreased by 1, and the variable that's passed into the procedure is initialized to "empty" (nil, but that's an implementation detail) 

This produces the following assembly

# [195] theResult                     := theSession.GetEvent
	leal	-24(%ebp),%eax
	call	FPC_INTF_DECR_REF
	leal	-24(%ebp),%eax
	pushl	%eax
	pushl	$0
	pushl	-8(%ebp)
	movl	-8(%ebp),%edx
	testl	%edx,%edx
	jne	.Lj260
	movl	$210,%eax
	call	FPC_HANDLEERROR
.Lj260:
	movl	(%edx),%eax
	call	*12(%eax)
	movl	%eax,-16(%ebp)

where FPC_INTF_DECR_REF is found in rtl/inc/objpas.inc

     procedure fpc_intf_decr_ref(var i: pointer);[public,alias: 'FPC_INTF_DECR_REF']; compilerproc;
       begin
         if assigned(i) then
           begin
             IUnknown(i)._Release;
             i:=nil;
           end;
       end;

So, before Windows returns a new value for ppEvent, the compiler emits a call to _Release, 
preventing a memory leak for our Pascal theEvent parameter.

case 2.

MF binding declaration

          function GetEvent
             (     dwFlags                   : DWORD;
               var ppEvent                   : IMFMediaEvent): HResult; stdcall;

Pascal code is the caller and Windows MF is the callee.

         theResult                     := theSession.GetEvent
           ( 0, theEvent);

The compiler sees the out parameter and (as you explained)

> * VAR: nothing happens to the reference count. A reference to the original variable is passed in, and changing it or reading it has exactly the same effect as changing/reading the original variable. 

This produces the following assembly

# [195] theResult                     := theSession.GetEvent
	leal	-24(%ebp),%eax
	pushl	%eax
	pushl	$0
	pushl	-8(%ebp)
	movl	-8(%ebp),%edx
	testl	%edx,%edx
	jne	.Lj260
	movl	$210,%eax
	call	FPC_HANDLEERROR

So, Windows returns a new value for ppEvent, the compiler does not emit a call to _Release, and our 
Pascal theEvent parameter leaks (except if you assume that Windows MF behaves different for both 
cases, i.c. if NIL is passed or not. This is highly unlikely, because the declaration in 
Mfobjects.idl is

     HRESULT GetEvent(
         [in] DWORD dwFlags,
         [out] IMFMediaEvent** ppEvent
         );

which ignores the input value <http://msdn.microsoft.com/en-us/library/hh916383.aspx>.

Regards,

Adriaan van Os




More information about the fpc-devel mailing list