diff --git a/glib/gtestutils.c b/glib/gtestutils.c
index 709c229ca..22b48d9ef 100644
--- a/glib/gtestutils.c
+++ b/glib/gtestutils.c
@@ -1690,6 +1690,7 @@ void
(*argv)[0], "G_TEST_TMPDIR");
exit (1);
}
+ _g_unset_cached_tmp_dir ();
/* And clear the traditional environment variables so subprocesses
* spawned by the code under test can’t trash anything. If a test
diff --git a/glib/gutils.c b/glib/gutils.c
index 186ef7d07..d73b6474b 100644
--- a/glib/gutils.c
+++ b/glib/gutils.c
@@ -598,6 +598,7 @@ static gchar *g_user_state_dir = NULL;
static gchar *g_user_runtime_dir = NULL;
static gchar **g_system_config_dirs = NULL;
static gchar **g_user_special_dirs = NULL;
+static gchar *g_tmp_dir = NULL;
/* fifteen minutes of fame for everybody */
#define G_USER_DIRS_EXPIRE 15 * 60
@@ -941,6 +942,17 @@ g_get_home_dir (void)
return home_dir;
}
+void
+_g_unset_cached_tmp_dir (void)
+{
+ G_LOCK (g_utils_global);
+ /* We have to leak the old value, as user code could be retaining pointers
+ * to it. */
+ g_ignore_leak (g_tmp_dir);
+ g_tmp_dir = NULL;
+ G_UNLOCK (g_utils_global);
+}
+
/**
* g_get_tmp_dir:
*
@@ -964,22 +976,33 @@ g_get_home_dir (void)
const gchar *
g_get_tmp_dir (void)
{
- static gchar *tmp_dir;
+ G_LOCK (g_utils_global);
- if (g_once_init_enter (&tmp_dir))
+ if (g_tmp_dir == NULL)
{
gchar *tmp;
-#ifdef G_OS_WIN32
- tmp = g_strdup (g_getenv ("TEMP"));
+ tmp = g_strdup (g_getenv ("G_TEST_TMPDIR"));
+ if (tmp == NULL || *tmp == '\0')
+ {
+ g_free (tmp);
+ tmp = g_strdup (g_getenv (
+#ifdef G_OS_WIN32
+ "TEMP"
+#else /* G_OS_WIN32 */
+ "TMPDIR"
+#endif /* G_OS_WIN32 */
+ ));
+ }
+
+#ifdef G_OS_WIN32
if (tmp == NULL || *tmp == '\0')
{
g_free (tmp);
tmp = get_windows_directory_root ();
}
#else /* G_OS_WIN32 */
- tmp = g_strdup (g_getenv ("TMPDIR"));
#ifdef P_tmpdir
if (tmp == NULL || *tmp == '\0')
@@ -1000,10 +1023,12 @@ g_get_tmp_dir (void)
}
#endif /* !G_OS_WIN32 */
- g_once_init_leave (&tmp_dir, tmp);
+ g_tmp_dir = g_steal_pointer (&tmp);
}
- return tmp_dir;
+ G_UNLOCK (g_utils_global);
+
+ return g_tmp_dir;
}
/**
diff --git a/glib/gutilsprivate.h b/glib/gutilsprivate.h
index f7d435d61..0d9b0df14 100644
--- a/glib/gutilsprivate.h
+++ b/glib/gutilsprivate.h
@@ -59,6 +59,8 @@ g_nearest_pow (gsize num)
return n + 1;
}
+void _g_unset_cached_tmp_dir (void);
+
G_END_DECLS
#endif /* __G_UTILS_PRIVATE_H__ */
diff --git a/glib/tests/fileutils.c b/glib/tests/fileutils.c
index 8ca25f9cc..b9149720d 100644
--- a/glib/tests/fileutils.c
+++ b/glib/tests/fileutils.c
@@ -1205,6 +1205,7 @@ test_dir_make_tmp (void)
name = g_dir_make_tmp ("testXXXXXXtest", &error);
g_assert_no_error (error);
g_assert_true (g_file_test (name, G_FILE_TEST_IS_DIR));
+ g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR")));
ret = g_rmdir (name);
g_assert_cmpint (ret, ==, 0);
g_free (name);
@@ -1212,6 +1213,7 @@ test_dir_make_tmp (void)
name = g_dir_make_tmp (NULL, &error);
g_assert_no_error (error);
g_assert_true (g_file_test (name, G_FILE_TEST_IS_DIR));
+ g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR")));
ret = g_rmdir (name);
g_assert_cmpint (ret, ==, 0);
g_free (name);
@@ -1238,6 +1240,7 @@ test_file_open_tmp (void)
g_assert_cmpint (fd, !=, -1);
g_assert_no_error (error);
g_assert_nonnull (name);
+ g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR")));
unlink (name);
g_free (name);
close (fd);
@@ -1246,6 +1249,7 @@ test_file_open_tmp (void)
g_assert_cmpint (fd, !=, -1);
g_assert_no_error (error);
g_assert_nonnull (name);
+ g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR")));
g_unlink (name);
g_free (name);
close (fd);
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index a01649b77..cec91a49a 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -158,6 +158,7 @@ glib_tests = {
'utils' : {
'c_standards': c_standards.keys(),
},
+ 'utils-isolated' : {},
'unicode' : {},
'unicode-encoding' : {},
'unicode-normalize': {},
diff --git a/glib/tests/utils-isolated.c b/glib/tests/utils-isolated.c
new file mode 100644
index 000000000..6ffb3424f
--- /dev/null
+++ b/glib/tests/utils-isolated.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2022 Marco Trevisan
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see .
+ *
+ * Author: Marco Trevisan
+ */
+
+#include "config.h"
+
+#include
+
+/* Test that all of the well-known directories returned by GLib
+ * are returned as children of test_tmpdir when running with
+ * %G_TEST_OPTION_ISOLATE_DIRS. This ensures that tests should
+ * not interfere with each other in `/tmp` while running.
+ */
+
+const char *test_tmpdir;
+
+static void
+test_tmp_dir (void)
+{
+ g_assert_cmpstr (g_get_tmp_dir (), ==, test_tmpdir);
+}
+
+static void
+test_home_dir (void)
+{
+ g_assert_true (g_str_has_prefix (g_get_home_dir (), test_tmpdir));
+}
+
+static void
+test_user_cache_dir (void)
+{
+ g_assert_true (g_str_has_prefix (g_get_user_cache_dir (), test_tmpdir));
+}
+
+static void
+test_system_config_dirs (void)
+{
+ const char *const *dir;
+
+ for (dir = g_get_system_config_dirs (); *dir != NULL; dir++)
+ g_assert_true (g_str_has_prefix (*dir, test_tmpdir));
+}
+
+static void
+test_user_config_dir (void)
+{
+ g_assert_true (g_str_has_prefix (g_get_user_config_dir (), test_tmpdir));
+}
+
+static void
+test_system_data_dirs (void)
+{
+ const char *const *dir;
+
+ for (dir = g_get_system_data_dirs (); *dir != NULL; dir++)
+ g_assert_true (g_str_has_prefix (*dir, test_tmpdir));
+}
+
+static void
+test_user_data_dir (void)
+{
+ g_assert_true (g_str_has_prefix (g_get_user_data_dir (), test_tmpdir));
+}
+
+static void
+test_user_state_dir (void)
+{
+ g_assert_true (g_str_has_prefix (g_get_user_state_dir (), test_tmpdir));
+}
+
+static void
+test_user_runtime_dir (void)
+{
+ g_assert_true (g_str_has_prefix (g_get_user_runtime_dir (), test_tmpdir));
+}
+
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_setenv ("LC_ALL", "C", TRUE);
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
+
+ test_tmpdir = g_getenv ("G_TEST_TMPDIR");
+ g_assert_nonnull (test_tmpdir);
+
+ g_test_add_func ("/utils-isolated/tmp-dir", test_tmp_dir);
+ g_test_add_func ("/utils-isolated/home-dir", test_home_dir);
+ g_test_add_func ("/utils-isolated/user-cache-dir", test_user_cache_dir);
+ g_test_add_func ("/utils-isolated/system-config-dirs", test_system_config_dirs);
+ g_test_add_func ("/utils-isolated/user-config-dir", test_user_config_dir);
+ g_test_add_func ("/utils-isolated/system-data-dirs", test_system_data_dirs);
+ g_test_add_func ("/utils-isolated/user-data-dir", test_user_data_dir);
+ g_test_add_func ("/utils-isolated/user-state-dir", test_user_state_dir);
+ g_test_add_func ("/utils-isolated/user-runtime-dir", test_user_runtime_dir);
+ return g_test_run ();
+}
diff --git a/tools/glib.supp b/tools/glib.supp
index 0609db44e..5b03557d9 100644
--- a/tools/glib.supp
+++ b/tools/glib.supp
@@ -840,6 +840,19 @@
fun:g_set_user_dirs
}
+# _g_unset_cached_tmp_dir() deliberately leaks the previous cached g_get_tmp_dir() values.
+# These will not all be reachable on exit.
+{
+ g_get_tmp_dir
+ Memcheck:Leak
+ match-leak-kinds:definite,reachable
+ fun:malloc
+ ...
+ fun:g_get_tmp_dir
+ ...
+ fun:g_test_init
+}
+
# g_get_system_data_dirs() caches a one-time allocation
{
g_get_system_data_dirs