diff --git a/glib/gdir.c b/glib/gdir.c index e15d353c5..073be6ad1 100644 --- a/glib/gdir.c +++ b/glib/gdir.c @@ -56,6 +56,7 @@ struct _GDir { + gatomicrefcount ref_count; #ifdef G_OS_WIN32 _WDIR *wdirp; #else @@ -89,10 +90,13 @@ GDir * g_dir_open_with_errno (const gchar *path, guint flags) { - GDir dir; #ifdef G_OS_WIN32 + GDir *dir; + _WDIR *wdirp; gint saved_errno; wchar_t *wpath; +#else + DIR *dirp; #endif g_return_val_if_fail (path != NULL, NULL); @@ -102,21 +106,27 @@ g_dir_open_with_errno (const gchar *path, g_return_val_if_fail (wpath != NULL, NULL); - dir.wdirp = _wopendir (wpath); + wdirp = _wopendir (wpath); saved_errno = errno; g_free (wpath); errno = saved_errno; - if (dir.wdirp == NULL) + if (wdirp == NULL) return NULL; + + dir = g_new0 (GDir, 1); + g_atomic_ref_count_init (&dir->ref_count); + dir->wdirp = wdirp; + + return g_steal_pointer (&dir); #else - dir.dirp = opendir (path); + dirp = opendir (path); - if (dir.dirp == NULL) + if (dirp == NULL) return NULL; -#endif - return g_memdup2 (&dir, sizeof dir); + return g_dir_new_from_dirp (dirp); +#endif } /** @@ -187,7 +197,8 @@ g_dir_new_from_dirp (gpointer dirp) g_return_val_if_fail (dirp != NULL, NULL); - dir = g_new (GDir, 1); + dir = g_new0 (GDir, 1); + g_atomic_ref_count_init (&dir->ref_count); dir->dirp = dirp; return dir; @@ -287,23 +298,84 @@ g_dir_rewind (GDir *dir) #endif } +static void +g_dir_actually_close (GDir *dir) +{ +#ifdef G_OS_WIN32 + g_clear_pointer (&dir->wdirp, _wclosedir); +#else + g_clear_pointer (&dir->dirp, closedir); +#endif +} + /** * g_dir_close: * @dir: (transfer full): a #GDir* created by g_dir_open() * - * Closes the directory and deallocates all related resources. + * Closes the directory immediately and decrements the reference count. + * + * Once the reference count reaches zero, the `GDir` structure itself will be + * freed. Prior to GLib 2.80, `GDir` was not reference counted. + * + * It is an error to call any of the `GDir` methods other than + * [method@GLib.Dir.ref] and [method@GLib.Dir.unref] on a `GDir` after calling + * [method@GLib.Dir.close] on it. **/ void g_dir_close (GDir *dir) { g_return_if_fail (dir != NULL); -#ifdef G_OS_WIN32 - _wclosedir (dir->wdirp); -#else - closedir (dir->dirp); -#endif - g_free (dir); + g_dir_actually_close (dir); + g_dir_unref (dir); +} + +/** + * g_dir_ref: + * @dir: (transfer none): a `GDir` + * + * Increment the reference count of `dir`. + * + * Returns: (transfer full): the same pointer as `dir` + * Since: 2.80 + */ +GDir * +g_dir_ref (GDir *dir) +{ + g_return_val_if_fail (dir != NULL, NULL); + + g_atomic_ref_count_inc (&dir->ref_count); + return dir; +} + +/** + * g_dir_unref: + * @dir: (transfer full): a `GDir` + * + * Decrements the reference count of `dir`. + * + * Once the reference count reaches zero, the directory will be closed and all + * resources associated with it will be freed. If [method@GLib.Dir.close] is + * called when the reference count is greater than zero, the directory is closed + * but the `GDir` structure will not be freed until its reference count reaches + * zero. + * + * It is an error to call any of the `GDir` methods other than + * [method@GLib.Dir.ref] and [method@GLib.Dir.unref] on a `GDir` after calling + * [method@GLib.Dir.close] on it. + * + * Since: 2.80 + */ +void +g_dir_unref (GDir *dir) +{ + g_return_if_fail (dir != NULL); + + if (g_atomic_ref_count_dec (&dir->ref_count)) + { + g_dir_actually_close (dir); + g_free (dir); + } } #ifdef G_OS_WIN32 diff --git a/glib/gdir.h b/glib/gdir.h index 0d3ee82b3..1b59b276e 100644 --- a/glib/gdir.h +++ b/glib/gdir.h @@ -49,6 +49,11 @@ void g_dir_rewind (GDir *dir); GLIB_AVAILABLE_IN_ALL void g_dir_close (GDir *dir); +GLIB_AVAILABLE_IN_2_80 +GDir * g_dir_ref (GDir *dir); +GLIB_AVAILABLE_IN_2_80 +void g_dir_unref (GDir *dir); + G_END_DECLS #endif /* __G_DIR_H__ */ diff --git a/glib/gstdio.h b/glib/gstdio.h index 7acdb9cef..42c177b2a 100644 --- a/glib/gstdio.h +++ b/glib/gstdio.h @@ -57,7 +57,7 @@ typedef struct stat GStatBuf; #endif -#if defined(G_OS_UNIX) && !defined(G_STDIO_WRAP_ON_UNIX) +#if defined(G_OS_UNIX) && !defined(G_STDIO_WRAP_ON_UNIX) && !defined(__GI_SCANNER__) /* Just pass on to the system functions, so there's no potential for data * format mismatches, especially with large file interfaces. diff --git a/glib/tests/dir.c b/glib/tests/dir.c index 74dbe143d..641cee80a 100644 --- a/glib/tests/dir.c +++ b/glib/tests/dir.c @@ -41,6 +41,29 @@ test_dir_nonexisting (void) g_error_free (error); } +static void +test_dir_refcounting (void) +{ + GDir *dir; + GError *local_error = NULL; + + g_test_summary ("Test refcounting interactions with g_dir_close()"); + + /* Try keeping the `GDir` struct alive after closing it. */ + dir = g_dir_open (".", 0, &local_error); + g_assert_no_error (local_error); + + g_dir_ref (dir); + g_dir_close (dir); + g_dir_unref (dir); + + /* Test that dropping the last ref closes it. Any leak here should be caught + * when the test is run under valgrind. */ + dir = g_dir_open (".", 0, &local_error); + g_assert_no_error (local_error); + g_dir_unref (dir); +} + int main (int argc, char *argv[]) { @@ -48,6 +71,7 @@ main (int argc, char *argv[]) g_test_add_func ("/dir/read", test_dir_read); g_test_add_func ("/dir/nonexisting", test_dir_nonexisting); + g_test_add_func ("/dir/refcounting", test_dir_refcounting); return g_test_run (); } diff --git a/gobject/gboxed.c b/gobject/gboxed.c index 57df1e07f..8872ca433 100644 --- a/gobject/gboxed.c +++ b/gobject/gboxed.c @@ -139,6 +139,7 @@ G_DEFINE_BOXED_TYPE (GKeyFile, g_key_file, g_key_file_ref, g_key_file_unref) G_DEFINE_BOXED_TYPE (GMappedFile, g_mapped_file, g_mapped_file_ref, g_mapped_file_unref) G_DEFINE_BOXED_TYPE (GBookmarkFile, g_bookmark_file, g_bookmark_file_copy, g_bookmark_file_free) G_DEFINE_BOXED_TYPE (GHmac, g_hmac, g_hmac_ref, g_hmac_unref) +G_DEFINE_BOXED_TYPE (GDir, g_dir, g_dir_ref, g_dir_unref) G_DEFINE_BOXED_TYPE (GMainLoop, g_main_loop, g_main_loop_ref, g_main_loop_unref) G_DEFINE_BOXED_TYPE (GMainContext, g_main_context, g_main_context_ref, g_main_context_unref) @@ -153,6 +154,8 @@ G_DEFINE_BOXED_TYPE (GUri, g_uri, g_uri_ref, g_uri_unref) G_DEFINE_BOXED_TYPE (GOptionGroup, g_option_group, g_option_group_ref, g_option_group_unref) G_DEFINE_BOXED_TYPE (GPatternSpec, g_pattern_spec, g_pattern_spec_copy, g_pattern_spec_free); +G_DEFINE_BOXED_TYPE (GStrvBuilder, g_strv_builder, g_strv_builder_ref, g_strv_builder_unref); + /* This one can't use G_DEFINE_BOXED_TYPE (GStrv, g_strv, g_strdupv, g_strfreev) */ GType g_strv_get_type (void) diff --git a/gobject/glib-types.h b/gobject/glib-types.h index d1a34ba5a..76f746cf2 100644 --- a/gobject/glib-types.h +++ b/gobject/glib-types.h @@ -345,6 +345,24 @@ typedef gsize GType; */ #define G_TYPE_HMAC (g_hmac_get_type ()) +/** + * G_TYPE_DIR: + * + * The #GType for a boxed type holding a #GDir. + * + * Since: 2.80 + */ +#define G_TYPE_DIR (g_dir_get_type ()) + +/** + * G_TYPE_STRV_BUILDER: + * + * The #GType for a boxed type holding a #GStrvBuilder. + * + * Since: 2.80 + */ +#define G_TYPE_STRV_BUILDER (g_strv_builder_get_type ()) + GOBJECT_AVAILABLE_IN_ALL GType g_date_get_type (void) G_GNUC_CONST; GOBJECT_AVAILABLE_IN_ALL @@ -411,6 +429,10 @@ GOBJECT_AVAILABLE_IN_2_76 GType g_bookmark_file_get_type (void) G_GNUC_CONST; GOBJECT_AVAILABLE_IN_2_80 GType g_hmac_get_type (void) G_GNUC_CONST; +GOBJECT_AVAILABLE_IN_2_80 +GType g_dir_get_type (void) G_GNUC_CONST; +GOBJECT_AVAILABLE_IN_2_80 +GType g_strv_builder_get_type (void) G_GNUC_CONST; GOBJECT_DEPRECATED_FOR('G_TYPE_VARIANT') GType g_variant_get_gtype (void) G_GNUC_CONST;