[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