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 767976 - GFile: Add g_file_peek_path()
GFile: Add g_file_peek_path()
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: gio
unspecified
Other All
: Normal normal
: 2.56
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2016-06-23 12:48 UTC by Colin Walters
Modified: 2018-01-15 18:27 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
GFile: Add g_file_get_cpath() (5.28 KB, patch)
2016-06-23 12:48 UTC, Colin Walters
needs-work Details | Review
GFile: Add g_file_peek_path() (5.97 KB, patch)
2018-01-09 12:26 UTC, Philip Withnall
none Details | Review
GFile: Add g_file_peek_path() (6.42 KB, patch)
2018-01-11 11:28 UTC, Philip Withnall
none Details | Review
GFile: Add g_file_peek_path() (6.47 KB, patch)
2018-01-15 15:48 UTC, Philip Withnall
none Details | Review
GFile: Add g_file_peek_path() (6.52 KB, patch)
2018-01-15 15:57 UTC, Philip Withnall
committed Details | Review

Description Colin Walters 2016-06-23 12:48:32 UTC
I've been carrying `gs_file_get_path_cached()` in libgsystem and it
has seen a lot of use in the ostree and flatpak codebases.  There are
probably others too.

I think language bindings like Python/Gjs could also use this to avoid
an extra malloc (i.e. we could transparently replace
`g_file_get_path()` with `g_file_get_cpath()`.
Comment 1 Colin Walters 2016-06-23 12:48:39 UTC
Created attachment 330252 [details] [review]
GFile: Add g_file_get_cpath()
Comment 2 Colin Walters 2016-06-23 13:02:53 UTC
Note - I'm not totally sure about the value of this, because at least for OSTree I'm likely to rework things to avoid GFile internally for the most part.

I'm curious what Alex (flatpak) thinks though.
Comment 3 Philip Withnall 2017-11-16 11:28:47 UTC
I realise that since this was filed, libostree has moved away from GFile — and that’s totally appropriate for libostree and its level in the stack.

However, I think there’s definitely value in a g_file_get_cpath() method for everywhere higher up in the stack — it’s always annoyed me that g_file_get_path() does an extra malloc() and you have to worry about freeing it.

I’m not totally sold on the naming (people might ask ‘what is a cpath?’). Maybe g_file_peek_path() instead? Open to other suggestions.

Matthias, Alex, what do you think about this? I’d like to get it in (or resolved, in any case) for 2.56.
Comment 4 Philip Withnall 2018-01-04 12:24:18 UTC
(In reply to Philip Withnall from comment #3)
> Matthias, Alex, what do you think about this? I’d like to get it in (or
> resolved, in any case) for 2.56.

Ping?
Comment 5 Matthias Clasen 2018-01-05 19:09:26 UTC
I hate to add a name with a easily overlooked one-character different to the existing g_file_get_path

Can we be explicit and call it g_file_get_cached_path() ?
Comment 6 Philip Withnall 2018-01-05 19:32:15 UTC
(In reply to Matthias Clasen from comment #5)
> Can we be explicit and call it g_file_get_cached_path() ?

I’d marginally prefer g_file_peek_path() because it’s less typing (and this will be a commonly used function), but I definitely agree that get_cpath() is to be avoided.
Comment 7 Alexander Larsson 2018-01-08 15:56:48 UTC
I agree with g_file_peek_path()
Comment 8 Philip Withnall 2018-01-09 12:16:21 UTC
Review of attachment 330252 [details] [review]:

::: gio/gfile.c
@@ +576,3 @@
+    _file_path_quark = g_quark_from_static_string ("gio-file-path");
+
+  G_LOCK (pathname_cache);

Why is locking needed here?

@@ +615,3 @@
+ */
+const char *
+g_file_get_cpath (GFile *file)

Rename to g_file_peek_path().
Comment 9 Philip Withnall 2018-01-09 12:26:20 UTC
Created attachment 366545 [details] [review]
GFile: Add g_file_peek_path()

This is a variant of g_file_get_path() which returns a const string to
the caller, rather than transferring ownership.

I've been carrying `gs_file_get_path_cached()` in libgsystem and it
has seen a lot of use in the ostree and flatpak codebases.  There are
probably others too.

I think language bindings like Python/Gjs could also use this to avoid
an extra malloc (i.e. we could transparently replace
`g_file_get_path()` with `g_file_peek_path()`.

(Originally by Colin Walters. Tweaked by Philip Withnall to update to
2.56 and change the function name.)
Comment 10 Philip Withnall 2018-01-09 12:27:13 UTC
OK, here’s an updated version of the patch with my review comments (except the query about the need for locking) fixed, plus a few other trivial cleanups made.

Can someone else give this a review please? Colin, what’s the deal with the locking?
Comment 11 Colin Walters 2018-01-09 16:04:24 UTC
I suppose the API could not be threadsafe but I think you'd find a lot of people expect "const accessors" i.e. _get() functions to be threadsafe.  At least in libostree and rpm-ostree we end up passing GFiles into worker threads.  Or at least we used to - of course we're pretty fd-relative nowadays.
Comment 12 Philip Withnall 2018-01-09 16:18:07 UTC
I’m not quite sure I follow. What specific situations do you expect this locking to help in?
Comment 14 Philip Withnall 2018-01-11 11:28:41 UTC
Created attachment 366649 [details] [review]
GFile: Add g_file_peek_path()

This is a variant of g_file_get_path() which returns a const string to
the caller, rather than transferring ownership.

I've been carrying `gs_file_get_path_cached()` in libgsystem and it
has seen a lot of use in the ostree and flatpak codebases.  There are
probably others too.

I think language bindings like Python/Gjs could also use this to avoid
an extra malloc (i.e. we could transparently replace
`g_file_get_path()` with `g_file_peek_path()`.

(Originally by Colin Walters. Tweaked by Philip Withnall to update to
2.56 and change the function name.)
Comment 15 Philip Withnall 2018-01-11 11:29:13 UTC
(In reply to Colin Walters from comment #13)
> https://git.gnome.org/browse/libgsystem/commit/
> ?id=2d24676846e3058d7a03bc127591f9fdcc346fab

That explains things well, thanks. I’ve added a similar explanation in a comment. Patch ready for review again.
Comment 16 Matthias Clasen 2018-01-15 15:29:51 UTC
Review of attachment 366649 [details] [review]:

::: gio/gfile.c
@@ +598,3 @@
+          return NULL;
+        }
+      g_object_set_qdata_full ((GObject*)file, _file_path_quark, (char*)path, (GDestroyNotify)g_free);

This works, but I wonder if we really need a separate lock here - g_object_set_data _is_ threadsafe, and we have an atomic replace operation for it: g_object_replace_data which seems like it would be a perfect fit here.
Comment 17 Philip Withnall 2018-01-15 15:36:34 UTC
Review of attachment 366649 [details] [review]:

::: gio/gfile.c
@@ +598,3 @@
+          return NULL;
+        }
+      g_object_set_qdata_full ((GObject*)file, _file_path_quark, (char*)path, (GDestroyNotify)g_free);

Ah, yes, g_object_replace_data() would work well, thanks.
Comment 18 Colin Walters 2018-01-15 15:36:44 UTC
We don't want to replace the original data; a thread could be using it.  We want something like `g_object_set_data_if_not_exists`.
Comment 19 Philip Withnall 2018-01-15 15:48:19 UTC
(In reply to Colin Walters from comment #18)
> We don't want to replace the original data; a thread could be using it.  We
> want something like `g_object_set_data_if_not_exists`.

I spent 5 minutes thinking that, but g_object_replace_data() is actually a standard compare-and-swap: it only does the replacement if the old data in the GObject matches the old_data parameter passed in to g_object_replace_data().
Comment 20 Philip Withnall 2018-01-15 15:48:57 UTC
Created attachment 366840 [details] [review]
GFile: Add g_file_peek_path()

This is a variant of g_file_get_path() which returns a const string to
the caller, rather than transferring ownership.

I've been carrying `gs_file_get_path_cached()` in libgsystem and it
has seen a lot of use in the ostree and flatpak codebases.  There are
probably others too.

I think language bindings like Python/Gjs could also use this to avoid
an extra malloc (i.e. we could transparently replace
`g_file_get_path()` with `g_file_peek_path()`.

(Originally by Colin Walters. Tweaked by Philip Withnall to update to
2.56, change the function name and drop the locking.)
Comment 21 Philip Withnall 2018-01-15 15:51:19 UTC
(In reply to Philip Withnall from comment #20)
> Created attachment 366840 [details] [review] [review]
> GFile: Add g_file_peek_path()

Updated to use g_object_replace_qdata(). I’m currently testing it.
Comment 22 Philip Withnall 2018-01-15 15:57:28 UTC
Created attachment 366841 [details] [review]
GFile: Add g_file_peek_path()

This is a variant of g_file_get_path() which returns a const string to
the caller, rather than transferring ownership.

I've been carrying `gs_file_get_path_cached()` in libgsystem and it
has seen a lot of use in the ostree and flatpak codebases.  There are
probably others too.

I think language bindings like Python/Gjs could also use this to avoid
an extra malloc (i.e. we could transparently replace
`g_file_get_path()` with `g_file_peek_path()`.

(Originally by Colin Walters. Tweaked by Philip Withnall to update to
2.56, change the function name and drop the locking.)
Comment 23 Philip Withnall 2018-01-15 15:58:15 UTC
Updated to fix a compiler warning. Tested, and it works.
Comment 24 Colin Walters 2018-01-15 16:54:37 UTC
Review of attachment 366841 [details] [review]:

::: gio/gfile.c
@@ +599,3 @@
+
+      if (g_object_replace_qdata ((GObject *) file, _file_path_quark,
+                                  NULL, (gpointer) path,

OK I wouldn't have guessted that `_replace_qdata()` had those semantics.
Hmm so I had to think about this hard, but I think it's correct...
basically since we're passing NULL here for `olddata`, it will
never match, and hence we'll never replace existing data.

This is making me wonder though...what other software is relying
on those semantics?  *Nothing* in libglib today besides test cases
uses either API.  In a subset of git.gnome.org checkouts I have locally:

$ for x in *; do (cd $x && git grep g_object_replace_qdata | cat); done
pango/pango-context.c:      if (!g_object_replace_qdata (G_OBJECT (fontset), cache_quark, NULL,
pango/pangocairo-context.c:      if (!g_object_replace_qdata (G_OBJECT (context), context_info_quark, NULL,
$ 

Which...indeed looks logically the same, including passing NULL
for `olddata`.
Comment 25 Colin Walters 2018-01-15 17:05:32 UTC
Review of attachment 366841 [details] [review]:

Marking ACN from my side, though an optional bit would be to add a comment like:
/* By passing NULL here we ensure we never replace existing data */
or so?
Comment 26 Philip Withnall 2018-01-15 18:25:30 UTC
Review of attachment 366841 [details] [review]:

::: gio/gfile.c
@@ +599,3 @@
+
+      if (g_object_replace_qdata ((GObject *) file, _file_path_quark,
+                                  NULL, (gpointer) path,

Thanks for that analysis; looking at the Pango code reminded me I haven’t free the new path in the failure case of g_object_replace_qdata(). I’ll fix that before pushing.
Comment 27 Philip Withnall 2018-01-15 18:27:53 UTC
Attachment 366841 [details] pushed as 4808a95 - GFile: Add g_file_peek_path()