maildev@lists.thunderbird.net

Thunderbird email developers

View all threads

refcounting - which types to use ?

EW
Enrico Weigelt, metux IT consult
Wed, Aug 16, 2017 10:56 AM

Hi folks,

I'm still a bit confused on which types (nsCOMPtr, ...) to use when
exactly, in combination w/ IDLs.

Let's consider a case where an nsArray is created and returned
(as rval, not out-parameter):

IDL:

[scriptable, uuid(ea0d8b3d-a549-4666-82d8-3a82cee2a3f1)]
interface nsIAddrBookService : nsISupports
{
nsIArray FindRecipients(in AString name);
...
}

C++:

/* nsIArray FindRecipients (in AString name); */
NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & addr,
nsIArray * *_retval)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMutableArray> list =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*_retval = list;
return FillRecipients(addr, list);
}

I'd assume that do_CreateInstance() returns the object w/ refcnt=1.
The assignment to nsCOMPtr should increase refcount. Correct ?
When the function is left, the nsCOMPtr is destroyed, and refcnt
goes back to 1 (we now have a reference left in the caller's pointer
field).

Now the caller side: (inspired by various examples ...)

nsCOMPtr<nsIArray> list;
rv = addrBookService->FindRecipients(
splittedRecipients[j].mEmail,
getter_AddRefs(list));

I'd guess getter_AddRefs() causes refcnt to be increased again, so
we're back at 2. That could lead to a leak, when that function returns
(and doesn't pass the ref anywhere else).

So, should I use dont_AddRef() here ?

Is the situation different when using out parameter instead of rval ?

BTW: what happens when passing nsCOMPtr as a reference (w/o IDL) ?

Assume the following code:

void caller() {
nsCOMPtr<nsIFoo> ref;

 callee(ref);
 ref->Something();

}

void callee(nsCOMPtr & outref) {
nsCOMPtr<nsIFoo> myref = do_CreateInstance(...);
...
outref = myref;
}

I'd assume that the last line will fill the caller's ref field w/ the
pointer and increase the object's refcnt (so it's 2 now), then when
callee is left, its myref is destroyed and refcnt is back to 1.

Is that correct ?

--mtx

Hi folks, I'm still a bit confused on which types (nsCOMPtr, ...) to use when exactly, in combination w/ IDLs. Let's consider a case where an nsArray is created and returned (as rval, not out-parameter): IDL: [scriptable, uuid(ea0d8b3d-a549-4666-82d8-3a82cee2a3f1)] interface nsIAddrBookService : nsISupports { nsIArray FindRecipients(in AString name); ... } C++: /* nsIArray FindRecipients (in AString name); */ NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & addr, nsIArray * *_retval) { nsresult rv = NS_OK; nsCOMPtr<nsIMutableArray> list = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); *_retval = list; return FillRecipients(addr, list); } I'd assume that do_CreateInstance() returns the object w/ refcnt=1. The assignment to nsCOMPtr should increase refcount. Correct ? When the function is left, the nsCOMPtr is destroyed, and refcnt goes back to 1 (we now have a reference left in the caller's pointer field). Now the caller side: (inspired by various examples ...) nsCOMPtr<nsIArray> list; rv = addrBookService->FindRecipients( splittedRecipients[j].mEmail, getter_AddRefs(list)); I'd guess getter_AddRefs() causes refcnt to be increased again, so we're back at 2. That could lead to a leak, when that function returns (and doesn't pass the ref anywhere else). So, should I use dont_AddRef() here ? Is the situation different when using out parameter instead of rval ? BTW: what happens when passing nsCOMPtr as a reference (w/o IDL) ? Assume the following code: void caller() { nsCOMPtr<nsIFoo> ref; callee(ref); ref->Something(); } void callee(nsCOMPtr & outref) { nsCOMPtr<nsIFoo> myref = do_CreateInstance(...); ... outref = myref; } I'd assume that the last line will fill the caller's ref field w/ the pointer and increase the object's refcnt (so it's 2 now), then when callee is left, its myref is destroyed and refcnt is back to 1. Is that correct ? --mtx
JK
Jörg Knobloch
Wed, Aug 16, 2017 8:13 PM

Hello Enrico,

sadly our tech people don't have time for individual training sessions
via e-mail. There are plenty of correct code examples and that
ref-counting stuff is also documented:

https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Using_nsCOMPtr
https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Using_nsCOMPtr/Reference_Manual

The principle isn't hard to grasp, the object basically counts its
references and when the last one goes away, the object can be destroyed.
You can even print the reference count (mRefCnt) at any time you like.

You seem to have trouble understanding what a callee should do, so just
look at some, just picking one at random:

https://dxr.mozilla.org/comm-central/rev/89fef90ea52f78ac6e9a8d4b4fd8d157eefacaa4/mailnews/local/src/nsMailboxServer.cpp#24

On 16/08/2017 12:56, Enrico Weigelt, metux IT consult wrote:

I'd assume that do_CreateInstance() returns the object w/ refcnt=1.

No. The assignment to the nsCOMPtr causes a ref-count of 1. At the end
of the function, the pointer is destroyed, the ref-count drops to 0 and
the referenced object can also be destroyed.

void caller() {
    nsCOMPtr<nsIFoo> ref;

    callee(ref);

That is already non-standard since you'd use getter_AddRefs()

ref->Something();
}

void callee(nsCOMPtr & outref) {

Again, non-standard, use nsIFoo** here.

nsCOMPtr<nsIFoo> myref = do_CreateInstance(...);
    ...
    outref = myref;

Again, non-standard, you'd use NS_ADDREF(*outref = myref) here.

When the function exits, the object is left with one reference, since
the reference from myref dies and the one you added remains. In the
caller that reference dies when the caller exits.

Jörg.

Hello Enrico, sadly our tech people don't have time for individual training sessions via e-mail. There are plenty of correct code examples and that ref-counting stuff is also documented: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Using_nsCOMPtr https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Using_nsCOMPtr/Reference_Manual The principle isn't hard to grasp, the object basically counts its references and when the last one goes away, the object can be destroyed. You can even print the reference count (mRefCnt) at any time you like. You seem to have trouble understanding what a callee should do, so just look at some, just picking one at random: https://dxr.mozilla.org/comm-central/rev/89fef90ea52f78ac6e9a8d4b4fd8d157eefacaa4/mailnews/local/src/nsMailboxServer.cpp#24 On 16/08/2017 12:56, Enrico Weigelt, metux IT consult wrote: > I'd assume that do_CreateInstance() returns the object w/ refcnt=1. No. The assignment to the nsCOMPtr causes a ref-count of 1. At the end of the function, the pointer is destroyed, the ref-count drops to 0 and the referenced object can also be destroyed. > void caller() { >     nsCOMPtr<nsIFoo> ref; > >     callee(ref); That is already non-standard since you'd use getter_AddRefs() > ref->Something(); > } > > void callee(nsCOMPtr & outref) { Again, non-standard, use nsIFoo** here. > nsCOMPtr<nsIFoo> myref = do_CreateInstance(...); >     ... >     outref = myref; Again, non-standard, you'd use NS_ADDREF(*outref = myref) here. When the function exits, the object is left with one reference, since the reference from myref dies and the one you added remains. In the caller that reference dies when the caller exits. Jörg.
RK
R Kent James
Wed, Aug 16, 2017 8:28 PM

On 8/16/2017 3:56 AM, Enrico Weigelt, metux IT consult via Maildev wrote:

Hi folks,

I'm still a bit confused on which types (nsCOMPtr, ...) to use when
exactly, in combination w/ IDLs.

As a general rule, if you are going to access a component using the
methods in the idl, then you should use an nsCOMPtr. Only if you want to
create a component and use non-idl methods on it, you should use nsRefPtr.

Let's consider a case where an nsArray is created and returned
(as rval, not out-parameter):

IDL:

[scriptable, uuid(ea0d8b3d-a549-4666-82d8-3a82cee2a3f1)]
interface nsIAddrBookService : nsISupports
{
nsIArray FindRecipients(in AString name);
...
}

C++:

/* nsIArray FindRecipients (in AString name); */
NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString &
addr, nsIArray * *_retval)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMutableArray> list =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*_retval = list;
return FillRecipients(addr, list);
}

The typical pattern here would be to use .forget, see for example the
current code for NS_IMETHODIMP
nsAbManager::GetUserProfileDirectory(nsIFile **userDir). The returned
_retval needs to have an addref, which gets transferred from the local
to the return value with .forget Your example does not do the addref, so
would not be acceptable.

I'd assume that do_CreateInstance() returns the object w/ refcnt=1.
The assignment to nsCOMPtr should increase refcount. Correct ?
When the function is left, the nsCOMPtr is destroyed, and refcnt
goes back to 1 (we now have a reference left in the caller's pointer
field).

Now the caller side: (inspired by various examples ...)

nsCOMPtr<nsIArray> list;
rv = addrBookService->FindRecipients(
splittedRecipients[j].mEmail,
getter_AddRefs(list));

I'd guess getter_AddRefs() causes refcnt to be increased again, so
we're back at 2.

No, the pattern is that a getter is expected to return a ptr that has
addref done already (by the .forget in my example). "getter_AddRefs"
says that the gitter has already done the addref, so that the local
nsCOMPtr does not need to do it again.

So, should I use dont_AddRef() here ?

I have never used a dont_AddRef, and it should not typically be done in
routine idl component access.

Is the situation different when using out parameter instead of rval ?

Not in C++.

BTW: what happens when passing nsCOMPtr as a reference (w/o IDL) ?

Assume the following code:

void caller() {
nsCOMPtr<nsIFoo> ref;

 callee(ref);
 ref->Something();

}

callee(ref) is not a getter, so callee does no addref. But in your
example you have never assigned a value to ref, so you are passing a
null ptr, and this would crash at ref->Something().

void callee(nsCOMPtr & outref) {
nsCOMPtr<nsIFoo> myref = do_CreateInstance(...);
...
outref = myref;
}

Oh I see. We do not routinely use references for nsCOMPtr types, and I
do not know how it would react in this case. So I would reject this code
with an odd pattern in a review, and would need a very good explanation
of why this pattern was required before I would accept it.

:rkent

On 8/16/2017 3:56 AM, Enrico Weigelt, metux IT consult via Maildev wrote: > Hi folks, > > > I'm still a bit confused on which types (nsCOMPtr, ...) to use when > exactly, in combination w/ IDLs. As a general rule, if you are going to access a component using the methods in the idl, then you should use an nsCOMPtr. Only if you want to create a component and use non-idl methods on it, you should use nsRefPtr. > > Let's consider a case where an nsArray is created and returned > (as rval, not out-parameter): > > IDL: > > [scriptable, uuid(ea0d8b3d-a549-4666-82d8-3a82cee2a3f1)] > interface nsIAddrBookService : nsISupports > { > nsIArray FindRecipients(in AString name); > ... > } > > C++: > > /* nsIArray FindRecipients (in AString name); */ > NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & > addr, nsIArray * *_retval) > { > nsresult rv = NS_OK; > nsCOMPtr<nsIMutableArray> list = > do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); > NS_ENSURE_SUCCESS(rv, rv); > *_retval = list; > return FillRecipients(addr, list); > } The typical pattern here would be to use .forget, see for example the current code for NS_IMETHODIMP nsAbManager::GetUserProfileDirectory(nsIFile **userDir). The returned _retval needs to have an addref, which gets transferred from the local to the return value with .forget Your example does not do the addref, so would not be acceptable. > > I'd assume that do_CreateInstance() returns the object w/ refcnt=1. > The assignment to nsCOMPtr should increase refcount. Correct ? > When the function is left, the nsCOMPtr is destroyed, and refcnt > goes back to 1 (we now have a reference left in the caller's pointer > field). > > Now the caller side: (inspired by various examples ...) > > nsCOMPtr<nsIArray> list; > rv = addrBookService->FindRecipients( > splittedRecipients[j].mEmail, > getter_AddRefs(list)); > > I'd guess getter_AddRefs() causes refcnt to be increased again, so > we're back at 2. No, the pattern is that a getter is expected to return a ptr that has addref done already (by the .forget in my example). "getter_AddRefs" says that the gitter has already done the addref, so that the local nsCOMPtr does not need to do it again. > So, should I use dont_AddRef() here ? I have never used a dont_AddRef, and it should not typically be done in routine idl component access. > > Is the situation different when using out parameter instead of rval ? Not in C++. > > > BTW: what happens when passing nsCOMPtr as a reference (w/o IDL) ? > > Assume the following code: > > void caller() { > nsCOMPtr<nsIFoo> ref; > > callee(ref); > ref->Something(); > } callee(ref) is not a getter, so callee does no addref. But in your example you have never assigned a value to ref, so you are passing a null ptr, and this would crash at ref->Something(). > > void callee(nsCOMPtr & outref) { > nsCOMPtr<nsIFoo> myref = do_CreateInstance(...); > ... > outref = myref; > } Oh I see. We do not routinely use references for nsCOMPtr types, and I do not know how it would react in this case. So I would reject this code with an odd pattern in a review, and would need a very good explanation of why this pattern was required before I would accept it. :rkent
SC
Shih-Chiang Chien
Thu, Aug 17, 2017 2:03 AM

You should use |forget| to transfer the ownership of the nsIArray from list
to _retval.

/* nsIArray FindRecipients (in AString name); */
NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & addr,
nsIArray * *_retval)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMutableArray> list =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);

if (NS_FAILED(rv = FillRecipients(addr, list)) {
  return rv;
}

list.forget(_retval);
return NS_OK;

}

On Wed, Aug 16, 2017 at 6:56 PM, Enrico Weigelt, metux IT consult <
enrico.weigelt@gr13.net> wrote:

Hi folks,

I'm still a bit confused on which types (nsCOMPtr, ...) to use when
exactly, in combination w/ IDLs.

Let's consider a case where an nsArray is created and returned
(as rval, not out-parameter):

IDL:

[scriptable, uuid(ea0d8b3d-a549-4666-82d8-3a82cee2a3f1)]
interface nsIAddrBookService : nsISupports
{
nsIArray FindRecipients(in AString name);
...
}

C++:

/* nsIArray FindRecipients (in AString name); */
NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & addr,
nsIArray * *_retval)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMutableArray> list =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
*_retval = list;

return FillRecipients(addr, list);

}

I'd assume that do_CreateInstance() returns the object w/ refcnt=1.
The assignment to nsCOMPtr should increase refcount. Correct ?
When the function is left, the nsCOMPtr is destroyed, and refcnt
goes back to 1 (we now have a reference left in the caller's pointer
field).

Now the caller side: (inspired by various examples ...)

nsCOMPtr<nsIArray> list;
rv = addrBookService->FindRecipients(
splittedRecipients[j].mEmail,
getter_AddRefs(list));

I'd guess getter_AddRefs() causes refcnt to be increased again, so
we're back at 2. That could lead to a leak, when that function returns
(and doesn't pass the ref anywhere else).

So, should I use dont_AddRef() here ?

Is the situation different when using out parameter instead of rval ?

BTW: what happens when passing nsCOMPtr as a reference (w/o IDL) ?

Assume the following code:

void caller() {
nsCOMPtr<nsIFoo> ref;

 callee(ref);
 ref->Something();

}

void callee(nsCOMPtr & outref) {
nsCOMPtr<nsIFoo> myref = do_CreateInstance(...);
...
outref = myref;
}

I'd assume that the last line will fill the caller's ref field w/ the
pointer and increase the object's refcnt (so it's 2 now), then when
callee is left, its myref is destroyed and refcnt is back to 1.

Is that correct ?

--mtx


dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

You should use |forget| to transfer the ownership of the nsIArray from list to _retval. /* nsIArray FindRecipients (in AString name); */ NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & addr, nsIArray * *_retval) { nsresult rv = NS_OK; nsCOMPtr<nsIMutableArray> list = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); if (NS_FAILED(rv = FillRecipients(addr, list)) { return rv; } list.forget(_retval); return NS_OK; } On Wed, Aug 16, 2017 at 6:56 PM, Enrico Weigelt, metux IT consult < enrico.weigelt@gr13.net> wrote: > Hi folks, > > > I'm still a bit confused on which types (nsCOMPtr, ...) to use when > exactly, in combination w/ IDLs. > > Let's consider a case where an nsArray is created and returned > (as rval, not out-parameter): > > IDL: > > [scriptable, uuid(ea0d8b3d-a549-4666-82d8-3a82cee2a3f1)] > interface nsIAddrBookService : nsISupports > { > nsIArray FindRecipients(in AString name); > ... > } > > C++: > > /* nsIArray FindRecipients (in AString name); */ > NS_IMETHODIMP nsAddrBookService::FindRecipients(const nsAString & addr, > nsIArray * *_retval) > { > nsresult rv = NS_OK; > nsCOMPtr<nsIMutableArray> list = > do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); > NS_ENSURE_SUCCESS(rv, rv); > *_retval = list; > return FillRecipients(addr, list); > } > > I'd assume that do_CreateInstance() returns the object w/ refcnt=1. > The assignment to nsCOMPtr should increase refcount. Correct ? > When the function is left, the nsCOMPtr is destroyed, and refcnt > goes back to 1 (we now have a reference left in the caller's pointer > field). > > Now the caller side: (inspired by various examples ...) > > nsCOMPtr<nsIArray> list; > rv = addrBookService->FindRecipients( > splittedRecipients[j].mEmail, > getter_AddRefs(list)); > > I'd guess getter_AddRefs() causes refcnt to be increased again, so > we're back at 2. That could lead to a leak, when that function returns > (and doesn't pass the ref anywhere else). > > So, should I use dont_AddRef() here ? > > Is the situation different when using out parameter instead of rval ? > > BTW: what happens when passing nsCOMPtr as a reference (w/o IDL) ? > > Assume the following code: > > void caller() { > nsCOMPtr<nsIFoo> ref; > > callee(ref); > ref->Something(); > } > > void callee(nsCOMPtr & outref) { > nsCOMPtr<nsIFoo> myref = do_CreateInstance(...); > ... > outref = myref; > } > > I'd assume that the last line will fill the caller's ref field w/ the > pointer and increase the object's refcnt (so it's 2 now), then when > callee is left, its myref is destroyed and refcnt is back to 1. > > Is that correct ? > > > --mtx > > _______________________________________________ > dev-platform mailing list > dev-platform@lists.mozilla.org > https://lists.mozilla.org/listinfo/dev-platform >
EW
Enrico Weigelt, metux IT consult
Thu, Aug 17, 2017 4:07 AM

On 17.08.2017 04:03, Shih-Chiang Chien wrote:

You should use |forget| to transfer the ownership of the nsIArray from
list to _retval.

Ok, thanks. Already suspected that (found some similar things in the
code). Could we update the docs (maybe some set of examples) ?

IIRC, there're some places that do it like in my prev mail, which I took
as example (just forget where exactly that was) - should that be fixed ?

By the way: is there a difference between out parameters and rval ?
Or can I just assume, rval's are equivalent to an out parameter ?

OTOH, is there a way to create versions that really return it as rval,
so I conveniently could just call like that ?

nsCOMPtr<nsIMutableArray> list = addrbook->FindRecipients(addr);

--mtx

On 17.08.2017 04:03, Shih-Chiang Chien wrote: > You should use |forget| to transfer the ownership of the nsIArray from > list to _retval. Ok, thanks. Already suspected that (found some similar things in the code). Could we update the docs (maybe some set of examples) ? IIRC, there're some places that do it like in my prev mail, which I took as example (just forget where exactly that was) - should that be fixed ? By the way: is there a difference between out parameters and rval ? Or can I just assume, rval's are equivalent to an out parameter ? OTOH, is there a way to create versions that really return it as rval, so I conveniently could just call like that ? nsCOMPtr<nsIMutableArray> list = addrbook->FindRecipients(addr); --mtx
AG
Aryeh Gregor
Thu, Aug 17, 2017 11:02 AM

On Thu, Aug 17, 2017 at 7:07 AM, Enrico Weigelt, metux IT consult
enrico.weigelt@gr13.net wrote:

OTOH, is there a way to create versions that really return it as rval,
so I conveniently could just call like that ?

nsCOMPtr<nsIMutableArray> list = addrbook->FindRecipients(addr);

You can do this with a return type of
already_AddRefed<nsIMutableArray>.  Then instead of

list.forget(_retval);
return NS_OK;

you do just

return list.forget();

Note that in this case you cannot ignore the return value -- it must
be assigned to an nsCOMPtr or a RefPtr, or else it will leak.  Ideally
we would allow a return type of nsCOMPtr<nsIMutableArray>&&, but
currently I think we don't.

On Thu, Aug 17, 2017 at 7:07 AM, Enrico Weigelt, metux IT consult <enrico.weigelt@gr13.net> wrote: > OTOH, is there a way to create versions that really return it as rval, > so I conveniently could just call like that ? > > nsCOMPtr<nsIMutableArray> list = addrbook->FindRecipients(addr); You can do this with a return type of already_AddRefed<nsIMutableArray>. Then instead of list.forget(_retval); return NS_OK; you do just return list.forget(); Note that in this case you cannot ignore the return value -- it must be assigned to an nsCOMPtr or a RefPtr, or else it will leak. Ideally we would allow a return type of nsCOMPtr<nsIMutableArray>&&, but currently I think we don't.