From 964d75ebe37ba879d05fa1b27f06aed63e7106db Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Thu, 12 Jul 2007 15:07:52 +0000 Subject: [PATCH] migrate per-thread magazine caches from single-thread scenario to first Thu Jul 12 15:46:40 2007 Tim Janik * glib/gslice.c: migrate per-thread magazine caches from single-thread scenario to first thread using GSlice after g_thread_init(); based on a patch by Tor Lillqvist, fixes #331853. removed warning about g_thread_init() being called after other glib functions (in particular g_slice* calls), because GSlice can cope with this now and the rest of glib is believed to cope as well. * tests/slice-threadinit.c: new test program which tests GSlice working across g_thread_init() calls. svn path=/trunk/; revision=5629 --- ChangeLog | 12 ++++ glib/gslice.c | 53 ++++++++++----- tests/Makefile.am | 3 + tests/slice-threadinit.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 16 deletions(-) create mode 100644 tests/slice-threadinit.c diff --git a/ChangeLog b/ChangeLog index 68ece8c..ee2e81c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +Thu Jul 12 15:46:40 2007 Tim Janik + + * glib/gslice.c: migrate per-thread magazine caches from single-thread + scenario to first thread using GSlice after g_thread_init(); based on + a patch by Tor Lillqvist, fixes #331853. + removed warning about g_thread_init() being called after other glib + functions (in particular g_slice* calls), because GSlice can cope + with this now and the rest of glib is believed to cope as well. + + * tests/slice-threadinit.c: new test program which tests GSlice working + across g_thread_init() calls. + 2007-07-10 Matthias Clasen * glib/pltcheck.sh: Add g_once_init_enter to the whitelist of diff --git a/glib/gslice.c b/glib/gslice.c index 1c0fecd..66ed898 100644 --- a/glib/gslice.c +++ b/glib/gslice.c @@ -372,20 +372,14 @@ void _g_slice_thread_init_nomessage (void) { /* we may not use g_error() or friends here */ - if (sys_page_size) - { - const char *pname; - - /* mem_error ("g_thread_init() must be called before GSlice is used, memory corrupted..."); */ - fputs ("\n***MEMORY-WARNING***: ", stderr); - pname = g_get_prgname(); - fprintf (stderr, "%s[%u]: GSlice: ", pname ? pname : "", getpid()); - fputs ("g_thread_init() must be called before all other GLib functions; " - "memory corruption due to late invocation of g_thread_init() has been detected; " - "this program is likely to crash, leak or unexpectedly abort soon...\n", stderr); - } if (!sys_page_size) g_slice_init_nomessage(); + else + { + /* g_slice_init_nomessage() has been called already, probably due + * to a g_slice_alloc1() before g_thread_init(). + */ + } private_thread_memory = g_private_new (private_thread_memory_cleanup); allocator->magazine_mutex = g_mutex_new(); allocator->slab_mutex = g_mutex_new(); @@ -429,11 +423,38 @@ thread_memory_from_self (void) ThreadMemory *tmem = g_private_get (private_thread_memory); if (G_UNLIKELY (!tmem)) { - const guint n_magazines = MAX_SLAB_INDEX (allocator); - tmem = g_malloc0 (sizeof (ThreadMemory) + sizeof (Magazine) * 2 * n_magazines); - tmem->magazine1 = (Magazine*) (tmem + 1); - tmem->magazine2 = &tmem->magazine1[n_magazines]; + static ThreadMemory *single_thread_memory = NULL; /* remember single-thread info for multi-threaded case */ + if (single_thread_memory && g_thread_supported ()) + { + g_mutex_lock (allocator->slab_mutex); + if (single_thread_memory) + { + /* GSlice has been used before g_thread_init(), and now + * we are running threaded. to cope with it, use the saved + * thread memory structure from when we weren't threaded. + */ + tmem = single_thread_memory; + single_thread_memory = NULL; /* slab_mutex protected when multi-threaded */ + } + g_mutex_unlock (allocator->slab_mutex); + } + if (!tmem) + { + const guint n_magazines = MAX_SLAB_INDEX (allocator); + tmem = g_malloc0 (sizeof (ThreadMemory) + sizeof (Magazine) * 2 * n_magazines); + tmem->magazine1 = (Magazine*) (tmem + 1); + tmem->magazine2 = &tmem->magazine1[n_magazines]; + } + /* g_private_get/g_private_set works in the single-threaded xor the multi- + * threaded case. but not *across* g_thread_init(), after multi-thread + * initialization it returns NULL for previously set single-thread data. + */ g_private_set (private_thread_memory, tmem); + /* save single-thread thread memory structure, in case we need to + * pick it up again after multi-thread initialization happened. + */ + if (!single_thread_memory && !g_thread_supported ()) + single_thread_memory = tmem; /* no slab_mutex created yet */ } return tmem; } diff --git a/tests/Makefile.am b/tests/Makefile.am index f4ab9b3..8919399 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -104,6 +104,7 @@ test_programs = \ slist-test \ slice-test \ slice-color \ + slice-threadinit \ spawn-test \ $(spawn_test_win32_gui) \ strfunc-test \ @@ -175,6 +176,8 @@ slice_test_SOURCES = slice-test.c memchunks.c slice_test_LDADD = $(thread_ldadd) slice_color_SOURCES = slice-color.c memchunks.c slice_color_LDADD = $(thread_ldadd) +slice_threadinit_SOURCES = slice-threadinit.c +slice_threadinit_LDADD = $(thread_ldadd) spawn_test_LDADD = $(progs_ldadd) strfunc_test_LDADD = $(progs_ldadd) string_test_LDADD = $(progs_ldadd) diff --git a/tests/slice-threadinit.c b/tests/slice-threadinit.c new file mode 100644 index 0000000..8c0b032 --- /dev/null +++ b/tests/slice-threadinit.c @@ -0,0 +1,166 @@ +/* slice-threadinit.c - test GSlice across g_thread_init + * Copyright (C) 2007 Tim Janik + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work 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. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ +#include + +#define N_PAGES (101) /* number of pages to sample */ +#define SAMPLE_SIZE (7) +#define PAGE_SIZE (128) /* must be <= minimum GSlice alignment block */ +#define MAGAZINE_PROBES { 77, 265, 347 } /* block sizes hopefully unused by g_thread_init */ +#define MAX_PROBE_TRIALS (1031) /* must be >= maximum magazine size */ + +#define ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base))) + +static struct { + void *page; + void *sample; +} pages[N_PAGES] = { { NULL, }, }; + +static const guint magazine_probes[] = MAGAZINE_PROBES; +#define N_MAGAZINE_PROBES G_N_ELEMENTS (magazine_probes) + +static void +release_trash_list (GSList **trash_list, + gsize block_size) +{ + while (*trash_list) + { + g_slice_free1 (block_size, (*trash_list)->data); + *trash_list = g_slist_delete_link (*trash_list, *trash_list); + } +} + +static GSList *free_list = NULL; + +static gboolean +allocate_from_known_page (void) +{ + guint i, j, n_trials = N_PAGES * PAGE_SIZE / SAMPLE_SIZE; /* upper bound */ + for (i = 0; i < n_trials; i++) + { + void *b = g_slice_alloc (SAMPLE_SIZE); + void *p = (void*) (PAGE_SIZE * ((gsize) b / PAGE_SIZE)); + free_list = g_slist_prepend (free_list, b); + /* find page */ + for (j = 0; j < N_PAGES; j++) + if (pages[j].page == p) + return TRUE; + } + return FALSE; +} + +int +main (int argc, + char *argv[]) +{ + int j, n_pages = 0; + void *mps[N_MAGAZINE_PROBES]; + + /* probe some magazine sizes */ + for (j = 0; j < N_MAGAZINE_PROBES; j++) + mps[j] = g_slice_alloc (magazine_probes[j]); + /* mps[*] now contains pointers to allocated slices */ + + /* allocate blocks from N_PAGES different pages */ + while (n_pages < N_PAGES) + { + void *b = g_slice_alloc (SAMPLE_SIZE); + void *p = (void*) (PAGE_SIZE * ((gsize) b / PAGE_SIZE)); + for (j = 0; j < N_PAGES; j++) + if (pages[j].page == p) + break; + if (j < N_PAGES) /* known page */ + free_list = g_slist_prepend (free_list, b); + else /* new page */ + { + j = n_pages++; + pages[j].page = p; + pages[j].sample = b; + } + } + /* release intermediate allocations */ + release_trash_list (&free_list, SAMPLE_SIZE); + + /* ensure that we can allocate from known pages */ + if (!allocate_from_known_page()) + g_error ("failed to allocate from magazine/page cache (before g_thread_init)"); + /* release intermediate allocations */ + release_trash_list (&free_list, SAMPLE_SIZE); + + /* release magazine probes to be retained */ + for (j = 0; j < N_MAGAZINE_PROBES; j++) + g_slice_free1 (magazine_probes[j], mps[j]); + /* mps[*] now contains pointers to releaed slices */ + + /* ensure probes were retained */ + for (j = 0; j < N_MAGAZINE_PROBES; j++) + { + GSList *trash = NULL; + guint k; + for (k = 0; k < MAX_PROBE_TRIALS; k++) + { + void *mem = g_slice_alloc (magazine_probes[j]); + if (mem == mps[j]) + break; /* reallocated previously freed slice */ + trash = g_slist_prepend (trash, mem); + } + release_trash_list (&trash, magazine_probes[j]); + if (k >= MAX_PROBE_TRIALS) /* failed to reallocate slice */ + g_error ("failed to reallocate slice from magazine (before g_thread_init): size=%d", magazine_probes[j]); + } + /* mps[*] now contains pointers to reallocated slices */ + + /* release magazine probes to be retained across g_thread_init */ + for (j = 0; j < N_MAGAZINE_PROBES; j++) + g_slice_free1 (magazine_probes[j], mps[j]); + /* mps[*] now contains pointers to releaed slices */ + + /* initialize threading (should retain allocator state) */ + g_thread_init (NULL); + + /* ensure probes were retained */ + for (j = 0; j < N_MAGAZINE_PROBES; j++) + { + GSList *trash = NULL; + guint k; + for (k = 0; k < MAX_PROBE_TRIALS; k++) + { + void *mem = g_slice_alloc (magazine_probes[j]); + if (mem == mps[j]) + break; /* reallocated previously freed slice */ + trash = g_slist_prepend (trash, mem); + } + release_trash_list (&trash, magazine_probes[j]); + if (k >= MAX_PROBE_TRIALS) /* failed to reallocate slice */ + g_error ("failed to reallocate slice from magazine (after g_thread_init): size=%d", magazine_probes[j]); + } + /* mps[*] now contains pointers to reallocated slices */ + + /* ensure that we can allocate from known pages */ + if (!allocate_from_known_page()) + g_error ("failed to allocate from magazine/page cache (after g_thread_init)"); + + /* some cleanups */ + for (j = 0; j < N_MAGAZINE_PROBES; j++) + g_slice_free1 (magazine_probes[j], mps[j]); + release_trash_list (&free_list, SAMPLE_SIZE); + + return 0; +} -- 2.7.4