After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 752812 - Add "release" method to RefPtr class
Add "release" method to RefPtr class
Status: RESOLVED FIXED
Product: glibmm
Classification: Bindings
Component: general
unspecified
Other Linux
: Normal normal
: ---
Assigned To: gtkmm-forge
gtkmm-forge
Depends on:
Blocks:
 
 
Reported: 2015-07-24 07:58 UTC by Marcin Kolny (IRC: loganek)
Modified: 2015-08-08 07:54 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
proposed solution (1.48 KB, patch)
2015-07-24 07:59 UTC, Marcin Kolny (IRC: loganek)
committed Details | Review
minor improvements (1.08 KB, patch)
2015-07-31 14:58 UTC, Marcin Kolny (IRC: loganek)
none Details | Review
minor improvements v2 (1.30 KB, patch)
2015-08-07 10:30 UTC, Marcin Kolny (IRC: loganek)
none Details | Review

Description Marcin Kolny (IRC: loganek) 2015-07-24 07:58:59 UTC
In case of transfer-full arguments, we have to write a tricky code to remove RefPtr's ownership:

auto raw_ptr = ref_ptr->gobj();
ref_ptr->reference();
ref_ptr.reset();
g_some_function_with_transfer_full (raw_ptr);

It's quite ugly solution, moreover, it can leads to unexpected behavior - we can't be sure, what actually *_ref method does (I know, usually it only increases refcount). 
"release" method allows us releasing refptr ownership, without additional increasing reference. Invalid usage of can be a reason of many memleaks, but the behavior is specified in documentation. I believe, everyone who will read it before use.
Comment 1 Marcin Kolny (IRC: loganek) 2015-07-24 07:59:39 UTC
Created attachment 308058 [details] [review]
proposed solution
Comment 2 Murray Cumming 2015-07-24 08:15:46 UTC
(In reply to Marcin Kolny (IRC: loganek) from comment #0)
> It's quite ugly solution, moreover, it can leads to unexpected behavior - we
> can't be sure, what actually *_ref method does (I know, usually it only
> increases refcount). 

When has this ever been an actual problem? Does reference() have some side-effect somewhere in gstreamer ?
Comment 3 Marcin Kolny (IRC: loganek) 2015-07-24 08:24:25 UTC
(In reply to Murray Cumming from comment #2)
> When has this ever been an actual problem? Does reference() have some
> side-effect somewhere in gstreamer ?
No, I've never seen reference method having side-effects, but it's not assured.
Comment 4 Murray Cumming 2015-07-24 08:40:54 UTC
Sorry, I'm not prepared to confuse the RefPtr<> API by adding this if it doesn't solve a real problem.

It seems OK to me to make the referencing explicit. These odd and unusual C functions want to take a reference, so that's what you give them. They don't usually really need you to no longer have any references yourself.
Comment 5 Marcin Kolny (IRC: loganek) 2015-07-27 09:14:49 UTC
Please consider following example:

  Glib::RefPtr<Gst::Buffer> ref_ptr = Gst::Buffer::create();

  // Thread 1:
  1. GstBuffer* raw_ptr = ref_ptr->gobj();
  2. ref_ptr->reference();
  3. ref_ptr.reset();
  4. g_some_function_with_transfer_full (raw_ptr);

  // Thread 2:
  5. GstBuffer *buffer = gst_buffer_make_writable(ref_ptr->gobj());
  6. // some operations on buffer

gst_buffer_make_writable method checks, if refcount equals 1, and if not, it creates a copy of an argument (https://developer.gnome.org/gstreamer/stable/gstreamer-GstBuffer.html#gst-buffer-make-writable).
Imagine, that application calls the lines in a following order: 1, 2, 5, 3, ... so gst_buffer_make_writable will make unnecessary copy of buffer. 

Moreover, according to bug: https://bugzilla.gnome.org/show_bug.cgi?id=752876, I've prepared a test:
  Glib::RefPtr<Something> something = Glib::RefPtr<SomethingDerived>(new SomethingDerived());
  g_assert_cmpint(something->max_ref_count(), ==, 1);
which should definitely pass if we have move constructors. Without "release" method, we're not able to write move constructor:
  template <class T_CastFrom> RefPtr(RefPtr<T_CastFrom>&& src);
and assignment operator properly.
I see your point; RefPtr is core class in glibmm library and we shouldn't add methods which are not really necessary. But "release" method IMO can't be replaced by any set of refptr's operations(ref_ptr->reference();ref_ptr.reset(); might cause side-effects).
Comment 6 Murray Cumming 2015-07-27 09:21:38 UTC
Thanks. Yes, I remember seeing something like that awful behaviour in gstreamer before. That's a terrible API. Luckily, it's the only time I've seen such a thing. Even in the best case, I don't see how C++ application code can easily make sure that the refcount of the underlying object is only 1. Hopefully you can hide this behaviour from users of the C++ API. 

By the way, is there any documentation for that function that mentions the refcount needing to be 1? It's not mentioned in the documentation that you linked to:
https://developer.gnome.org/gstreamer/stable/gstreamer-GstBuffer.html#gst-buffer-make-writable
or here:
http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/gstreamer-GstMiniObject.html#gst-mini-object-make-writable

I suggest that we revisit this when we have figured out what move constructors we will have in RefPtr and made sure that they work properly.
Comment 7 Marcin Kolny (IRC: loganek) 2015-07-27 09:40:26 UTC
(In reply to Murray Cumming from comment #6)
> By the way, is there any documentation for that function that mentions the
> refcount needing to be 1? It's not mentioned in the documentation that you
> linked to:
> https://developer.gnome.org/gstreamer/stable/gstreamer-GstBuffer.html#gst-
> buffer-make-writable
> or here:
> http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/
> gstreamer-GstMiniObject.html#gst-mini-object-make-writable
It's not mentioned in doc for *_make_writable methods, but in documentation of gst_mini_object_is_writable(http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/gstreamer-GstMiniObject.html#gst-mini-object-is-writable) you can find out, what "writable object" actually means. 
> 
> I suggest that we revisit this when we have figured out what move
> constructors we will have in RefPtr and made sure that they work properly.
OK, I'll try to write a tests for move constructor/assignment operator containing universal reference slightly different(not using max_ref_count). We can back here after we close a bug 752876.
Comment 8 Kjell Ahlstedt 2015-07-28 08:48:34 UTC
Is it safe to use __attribute__((warn_unused_result))? Will all supported
compilers either understand or ignore it?

When an __attribute__ is used in glib, it's used via a preprocessor macro, like

  #if    __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
  #define G_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
  #else
  #define G_GNUC_WARN_UNUSED_RESULT
  #endif /* __GNUC__ */

in glib/glib/gmacros.h.
Comment 9 Murray Cumming 2015-07-28 08:53:13 UTC
Sorry, I must have missed that. I would prefer to avoid using that for now. We could discuss that separately. If we use it later, I would definitely prefer to use the glib macro.
Comment 10 Marcin Kolny (IRC: loganek) 2015-07-28 10:52:17 UTC
(In reply to Kjell Ahlstedt from comment #8)
> Is it safe to use __attribute__((warn_unused_result))? Will all supported
> compilers either understand or ignore it?
You're right. E.g. msvc doesn't support this attribute (it has _Check_return_ instead) https://msdn.microsoft.com/en-us/library/jj159529.aspx, but I'm not sure if they ignore it. Anyway, I agree, we should use glib macro instead (if we keep this attribute at all)
Comment 11 Murray Cumming 2015-07-28 18:48:56 UTC
You only plan to use release() in the implementation of a gstreamermm method, right? Could you please show me a whole method that would use it, including the method signature and its implementation.
Comment 12 Marcin Kolny (IRC: loganek) 2015-07-29 05:32:10 UTC
(In reply to Murray Cumming from comment #11)
> You only plan to use release() in the implementation of a gstreamermm
> method, right? 
Not really, we could use it everywhere, for transfer-full arguments:

  template <class T> inline
  typename T::BaseObjectType* unwrap(Glib::RefPtr<T>&& o)
  {
    return o.release();
  }

And conversion definition:

  _CONVERSION(`Glib::RefPtr<Glib::SomeObject>&&',`GSomeObject*',`Glib::unwrap(std::move($3))')
Comment 13 Murray Cumming 2015-07-29 07:29:26 UTC
Could you please give me an example how the actual generated (or hand-written) code would look?

I think it would be unwise to create an unwrap() specialization that uses this special API. A simple call to release() would be less confusing.
Comment 14 Marcin Kolny (IRC: loganek) 2015-07-31 07:46:58 UTC
(In reply to Murray Cumming from comment #13)
> Could you please give me an example how the actual generated (or
> hand-written) code would look?

Sure, please consider e.g. gst_pad_push method. Declaration in .hg file:
_WRAP_METHOD(Gst::FlowReturn push(Glib::RefPtr<Gst::Buffer>&& buffer), gst_pad_push)

And output .cc file:
Gst::FlowReturn Pad::push(Glib::RefPtr<Gst::Buffer>&& buffer)
{
  return static_cast<Gst::FlowReturn>(gst_pad_push(buffer.release()->gobj()));
}
Comment 15 Murray Cumming 2015-07-31 08:08:59 UTC
So, this wouldn't compile?
  auto buffer = create_some_buffer(someargs);
  pad.push(buffer);
But this would?
  pad.push(create_some_buffer());
and this would?

Is there any way to hide the strangeness from the API? For instance:
  create_some_buffer_and_add_to_pad(someargs, pad);


If we knew that the created buffer would always be given to something with full ownership then I guess we could wrap the buffer in a unique_ptr<> somehow, but I doubt the gsreamer API is that clear.
Comment 16 Marcin Kolny (IRC: loganek) 2015-07-31 08:25:03 UTC
(In reply to Murray Cumming from comment #15)
> So, this wouldn't compile?
>   auto buffer = create_some_buffer(someargs);
>   pad.push(buffer);
> But this would?
>   pad.push(create_some_buffer());
> and this would?
First should work as well, if you add std::move(buffer) instead of buffer. 
> 
> Is there any way to hide the strangeness from the API? For instance:
>   create_some_buffer_and_add_to_pad(someargs, pad);
Not really.. sometimes we can have a buffer created somewhere else. Moreover, in gstreamer there is really a lot of functions (not only in Pad class, and not only with GstBuffer arguments) with transfer-full annotation. 
> 
> 
> If we knew that the created buffer would always be given to something with
> full ownership then I guess we could wrap the buffer in a unique_ptr<>
> somehow, but I doubt the gsreamer API is that clear.
AFAIK, there's no assumption about only-full-ownership buffer's transfer.
Comment 17 Murray Cumming 2015-07-31 08:36:47 UTC
OK. Kjell, do you see any alternative to adding RefPtr::release() to make this possible?
Comment 18 Kjell Ahlstedt 2015-07-31 14:02:41 UTC
No, I don't see any alternative.

If we add RefPtr::release(), it should have a warning in its documentation,
like Glib::ObjectBase::[un]reference().

Marcin wants to have RefPtr release the ownership of its managed object
without affecting the ref count of the object.

The code in comment 0 comes close. Many objects have a gobj_copy() method,
and then the code can be simplified:

  auto raw_ptr = ref_ptr->gobj_copy();
  ref_ptr.reset();
  g_some_function_with_transfer_full (raw_ptr);

Both versions of the code come close to what Marcin wants, but not close enough,
as explained in comment 5.

std::unique_ptr has a release() method, but std::shared_ptr has none.
There is an important difference between std::shared_ptr and Glib::RefPtr.
shared_ptr manages the ref counter itself. An unmanaged object would not have a
ref counter. RefPtr manages an object that contains its own ref counter.
Comment 19 Marcin Kolny (IRC: loganek) 2015-07-31 14:58:03 UTC
Created attachment 308558 [details] [review]
minor improvements

OK, I added a warning in comment, and replaced warn_unused_result attribute with Glib macro. Please review it. If everything is ok, I'll push it and close this report.
Comment 20 Kjell Ahlstedt 2015-07-31 15:27:54 UTC
I didn't mean that you should copy the warning verbatim from reference(), rather
write something suitable in the same spirit.

  Most users should not use release(). It can spoil the automatic destruction
  of the managed object. A legitimate use is if you immediately give RefPtr's
  reference to another object.

With this or a similar change I think it's ok, but give Murray a chance to
comment before the push.
Comment 21 Kjell Ahlstedt 2015-08-01 06:46:03 UTC
If RefPtr uses G_GNUC_WARN_UNUSED_RESULT, refptr.h must #include <glib.h>.
Comment 22 Murray Cumming 2015-08-01 19:30:38 UTC
I guess that's not the end of the world.
Comment 23 Kjell Ahlstedt 2015-08-07 08:27:20 UTC
Marcin, are you waiting for more comments before you make the final update of
the release() method?
Comment 24 Marcin Kolny (IRC: loganek) 2015-08-07 09:04:48 UTC
(In reply to Kjell Ahlstedt from comment #23)
> Marcin, are you waiting for more comments before you make the final update of
> the release() method?

No, sorry. I was a little bit busy last time, I'm updating it in a few days.
Comment 25 Marcin Kolny (IRC: loganek) 2015-08-07 10:30:11 UTC
Created attachment 308889 [details] [review]
minor improvements v2

I fixed my patch, by adding glib.h include directive, and using Kjell's comment. 
If you have no objections, I'll push it to the upstream.
Comment 26 Kjell Ahlstedt 2015-08-07 16:45:32 UTC
It's ok with me.
Comment 27 Marcin Kolny (IRC: loganek) 2015-08-08 07:54:16 UTC
OK, I've pushed my patch.