libsysprof: parallelize capture symbolizing

This commit is contained in:
Christian Hergert
2023-08-15 22:17:34 -07:00
parent d1b4308af7
commit a7a0c782d1
7 changed files with 228 additions and 136 deletions

View File

@ -28,6 +28,7 @@
struct _SysprofAddressLayout
{
GObject parent_instance;
GRWLock rwlock;
GPtrArray *mmaps;
guint mmaps_dirty : 1;
};
@ -73,6 +74,7 @@ sysprof_address_layout_finalize (GObject *object)
SysprofAddressLayout *self = (SysprofAddressLayout *)object;
g_clear_pointer (&self->mmaps, g_ptr_array_unref);
g_rw_lock_clear (&self->rwlock);
G_OBJECT_CLASS (sysprof_address_layout_parent_class)->finalize (object);
}
@ -88,6 +90,7 @@ sysprof_address_layout_class_init (SysprofAddressLayoutClass *klass)
static void
sysprof_address_layout_init (SysprofAddressLayout *self)
{
g_rw_lock_init (&self->rwlock);
self->mmaps = g_ptr_array_new_with_free_func (g_object_unref);
}
@ -104,9 +107,13 @@ sysprof_address_layout_take (SysprofAddressLayout *self,
g_return_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self));
g_return_if_fail (SYSPROF_IS_DOCUMENT_MMAP (map));
g_rw_lock_writer_lock (&self->rwlock);
g_ptr_array_add (self->mmaps, map);
self->mmaps_dirty = TRUE;
g_rw_lock_writer_unlock (&self->rwlock);
}
static int
@ -195,26 +202,42 @@ sysprof_address_layout_lookup (SysprofAddressLayout *self,
g_return_val_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self), NULL);
if (self->mmaps_dirty)
g_rw_lock_reader_lock (&self->rwlock);
while (self->mmaps_dirty)
{
g_autoptr(EggBitset) dups = NULL;
EggBitsetIter iter;
guint old_len = self->mmaps->len;
guint i;
self->mmaps_dirty = FALSE;
g_rw_lock_reader_unlock (&self->rwlock);
g_rw_lock_writer_lock (&self->rwlock);
g_ptr_array_sort (self->mmaps, compare_mmaps);
dups = find_duplicates (self->mmaps);
if (egg_bitset_iter_init_last (&iter, dups, &i))
if (self->mmaps_dirty)
{
do
g_ptr_array_remove_index (self->mmaps, i);
while (egg_bitset_iter_previous (&iter, &i));
self->mmaps_dirty = FALSE;
g_ptr_array_sort (self->mmaps, compare_mmaps);
dups = find_duplicates (self->mmaps);
if (egg_bitset_iter_init_last (&iter, dups, &i))
{
do
g_ptr_array_remove_index (self->mmaps, i);
while (egg_bitset_iter_previous (&iter, &i));
}
/* We can't be monitored when we're in this path as the
* application cannot have gotten access yet. Ignore
* any sort of items changes.
*
* g_list_model_items_changed (G_LIST_MODEL (self),
* 0, old_len, self->mmaps->len);
*/
}
g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, self->mmaps->len);
g_rw_lock_writer_unlock (&self->rwlock);
g_rw_lock_reader_lock (&self->rwlock);
}
ret = bsearch (&address,
@ -223,5 +246,7 @@ sysprof_address_layout_lookup (SysprofAddressLayout *self,
sizeof (gpointer),
find_by_address);
g_rw_lock_reader_unlock (&self->rwlock);
return ret ? *ret : NULL;
}

View File

@ -22,6 +22,8 @@
#include <glib/gi18n.h>
#include <libdex.h>
#include "sysprof-address-layout-private.h"
#include "sysprof-document-private.h"
#include "sysprof-document-symbols-private.h"
@ -70,6 +72,9 @@ typedef struct _Symbolize
ProgressFunc progress_func;
gpointer progress_data;
GDestroyNotify progress_data_destroy;
guint n_partitions;
guint partition_seq;
guint progress;
} Symbolize;
static void
@ -140,41 +145,108 @@ add_traceable (SysprofDocumentSymbols *self,
}
}
static void
sysprof_document_symbols_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
static DexFuture *
symbolize_fiber (gpointer user_data)
{
static const struct {
const char *name;
guint value;
} context_switches[] = {
{ "- - Hypervisor - -", SYSPROF_ADDRESS_CONTEXT_HYPERVISOR },
{ "- - Kernel - -", SYSPROF_ADDRESS_CONTEXT_KERNEL },
{ "- - User - -", SYSPROF_ADDRESS_CONTEXT_USER },
{ "- - Guest - -", SYSPROF_ADDRESS_CONTEXT_GUEST },
{ "- - Guest Kernel - -", SYSPROF_ADDRESS_CONTEXT_GUEST_KERNEL },
{ "- - Guest User - -", SYSPROF_ADDRESS_CONTEXT_GUEST_USER },
};
g_autoptr(GRefString) context_switch = g_ref_string_new_intern ("Context Switch");
Symbolize *state = task_data;
EggBitsetIter iter;
EggBitset *bitset;
GListModel *model;
guint count = 0;
guint i;
Symbolize *state = user_data;
g_autoptr(GListModel) model = NULL;
guint partition;
guint count;
guint n_items;
guint max;
g_assert (source_object == NULL);
g_assert (G_IS_TASK (task));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
g_assert (state != NULL);
g_assert (SYSPROF_IS_DOCUMENT (state->document));
g_assert (SYSPROF_IS_SYMBOLIZER (state->symbolizer));
g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (state->symbols));
g_assert (state->n_partitions > 1);
bitset = _sysprof_document_traceables (state->document);
model = G_LIST_MODEL (state->document);
if (SYSPROF_IS_NO_SYMBOLIZER (state->symbolizer))
return dex_future_new_for_boolean (TRUE);
model = sysprof_document_list_traceables (state->document);
n_items = g_list_model_get_n_items (model);
partition = g_atomic_int_add (&state->partition_seq, 1);
count = n_items / (state->n_partitions - 1);
max = MIN (n_items, (partition + 1) * count);
for (guint i = count * partition; i < max; i++)
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable));
SysprofProcessInfo *process_info = g_hash_table_lookup (state->pid_to_process_info, GINT_TO_POINTER (pid));
add_traceable (state->symbols,
state->strings,
process_info,
traceable,
state->symbolizer);
if (g_atomic_int_add (&state->progress, 1) % 100 == 0)
{
if (state->progress_func)
state->progress_func (state->progress / (double)n_items, _("Symbolizing stack traces"), state->progress_data);
}
}
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
handle_symbolize_result (DexFuture *completed,
gpointer user_data)
{
Symbolize *state = user_data;
if (dex_future_get_status (completed) == DEX_FUTURE_STATUS_REJECTED)
return NULL;
return dex_future_new_take_object (g_object_ref (state->symbols));
}
static const struct {
const char *name;
guint value;
} context_switches[] = {
{ "- - Hypervisor - -", SYSPROF_ADDRESS_CONTEXT_HYPERVISOR },
{ "- - Kernel - -", SYSPROF_ADDRESS_CONTEXT_KERNEL },
{ "- - User - -", SYSPROF_ADDRESS_CONTEXT_USER },
{ "- - Guest - -", SYSPROF_ADDRESS_CONTEXT_GUEST },
{ "- - Guest Kernel - -", SYSPROF_ADDRESS_CONTEXT_GUEST_KERNEL },
{ "- - Guest User - -", SYSPROF_ADDRESS_CONTEXT_GUEST_USER },
};
void
_sysprof_document_symbols_new (SysprofDocument *document,
SysprofStrings *strings,
SysprofSymbolizer *symbolizer,
GHashTable *pid_to_process_info,
ProgressFunc progress_func,
gpointer progress_data,
GDestroyNotify progress_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GRefString) context_switch = g_ref_string_new_intern ("Context Switch");
g_autoptr(DexAsyncResult) result = NULL;
g_autoptr(GPtrArray) futures = NULL;
DexFuture *future;
Symbolize *state;
g_return_if_fail (SYSPROF_IS_DOCUMENT (document));
g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer));
state = g_new0 (Symbolize, 1);
state->document = g_object_ref (document);
state->symbolizer = g_object_ref (symbolizer);
state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL);
state->strings = sysprof_strings_ref (strings);
state->pid_to_process_info = g_hash_table_ref (pid_to_process_info);
state->progress_func = progress_func;
state->progress_data = progress_data;
state->progress_data_destroy = progress_data_destroy;
state->n_partitions = MAX (1, g_get_num_processors () / 2) + 1;
/* Create static symbols for context switch use */
for (guint cs = 0; cs < G_N_ELEMENTS (context_switches); cs++)
@ -193,80 +265,36 @@ sysprof_document_symbols_worker (GTask *task,
state->symbols->context_switches[context_switches[cs].value] = g_steal_pointer (&symbol);
}
/* Walk through the available traceables which need symbols extracted */
if (!SYSPROF_IS_NO_SYMBOLIZER (state->symbolizer) &&
egg_bitset_iter_init_first (&iter, bitset, &i))
futures = g_ptr_array_new_with_free_func (dex_unref);
for (guint i = 0; i < state->n_partitions; i++)
{
guint n_items = egg_bitset_get_size (bitset);
DexScheduler *scheduler = dex_thread_pool_scheduler_get_default ();
do
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable));
SysprofProcessInfo *process_info = g_hash_table_lookup (state->pid_to_process_info, GINT_TO_POINTER (pid));
add_traceable (state->symbols,
state->strings,
process_info,
traceable,
state->symbolizer);
count++;
if (state->progress_func != NULL && count % 100 == 0)
state->progress_func (count / (double)n_items, _("Symbolizing stack traces"), state->progress_data);
}
while (egg_bitset_iter_next (&iter, &i));
g_ptr_array_add (futures,
dex_scheduler_spawn (scheduler, 0,
symbolize_fiber,
state,
NULL));
}
g_task_return_pointer (task,
g_object_ref (state->symbols),
g_object_unref);
}
future = dex_future_allv ((DexFuture **)futures->pdata, futures->len);
future = dex_future_finally (future,
handle_symbolize_result,
state,
(GDestroyNotify)symbolize_free);
void
_sysprof_document_symbols_new (SysprofDocument *document,
SysprofStrings *strings,
SysprofSymbolizer *symbolizer,
GHashTable *pid_to_process_info,
ProgressFunc progress_func,
gpointer progress_data,
GDestroyNotify progress_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
Symbolize *state;
g_return_if_fail (SYSPROF_IS_DOCUMENT (document));
g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer));
state = g_new0 (Symbolize, 1);
state->document = g_object_ref (document);
state->symbolizer = g_object_ref (symbolizer);
state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL);
state->strings = sysprof_strings_ref (strings);
state->pid_to_process_info = g_hash_table_ref (pid_to_process_info);
state->progress_func = progress_func;
state->progress_data = progress_data;
state->progress_data_destroy = progress_data_destroy;
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, _sysprof_document_symbols_new);
g_task_set_task_data (task, state, (GDestroyNotify)symbolize_free);
g_task_run_in_thread (task, sysprof_document_symbols_worker);
result = dex_async_result_new (NULL, cancellable, callback, user_data);
dex_async_result_await (result, future);
}
SysprofDocumentSymbols *
_sysprof_document_symbols_new_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (result), NULL);
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == _sysprof_document_symbols_new, NULL);
g_return_val_if_fail (DEX_IS_ASYNC_RESULT (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
return dex_async_result_propagate_pointer (DEX_ASYNC_RESULT (result), error);
}
/**

View File

@ -33,10 +33,10 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, SYSPROF, ELF_LOADER, GObject)
SysprofElfLoader *sysprof_elf_loader_new (void);
const char * const *sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self);
char **sysprof_elf_loader_dup_debug_dirs (SysprofElfLoader *self);
void sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self,
const char * const *debug_dirs);
const char * const *sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self);
char **sysprof_elf_loader_dup_external_debug_dirs (SysprofElfLoader *self);
void sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self,
const char * const *debug_dirs);
SysprofElf *sysprof_elf_loader_load (SysprofElfLoader *self,

View File

@ -32,6 +32,7 @@
struct _SysprofElfLoader
{
GObject parent_instance;
GRecMutex mutex;
GHashTable *cache;
char **debug_dirs;
char **external_debug_dirs;
@ -71,6 +72,7 @@ sysprof_elf_loader_finalize (GObject *object)
g_clear_pointer (&self->debug_dirs, g_strfreev);
g_clear_pointer (&self->external_debug_dirs, g_strfreev);
g_clear_pointer (&self->cache, g_hash_table_unref);
g_rec_mutex_clear (&self->mutex);
G_OBJECT_CLASS (sysprof_elf_loader_parent_class)->finalize (object);
}
@ -86,11 +88,11 @@ sysprof_elf_loader_get_property (GObject *object,
switch (prop_id)
{
case PROP_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_loader_get_debug_dirs (self));
g_value_take_boxed (value, sysprof_elf_loader_dup_debug_dirs (self));
break;
case PROP_EXTERNAL_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_loader_get_external_debug_dirs (self));
g_value_take_boxed (value, sysprof_elf_loader_dup_external_debug_dirs (self));
break;
default:
@ -149,12 +151,13 @@ sysprof_elf_loader_class_init (SysprofElfLoaderClass *klass)
static void
sysprof_elf_loader_init (SysprofElfLoader *self)
{
g_rec_mutex_init (&self->mutex);
self->debug_dirs = g_strdupv ((char **)DEFAULT_DEBUG_DIRS);
self->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _g_object_xunref);
}
/**
* sysprof_elf_loader_get_debug_dirs:
* sysprof_elf_loader_dup_debug_dirs:
* @self: a #SysprofElfLoader
*
* Gets the #SysprofElfLoader:debug-dirs property.
@ -164,12 +167,18 @@ sysprof_elf_loader_init (SysprofElfLoader *self)
*
* Returns: (nullable): an array of debug directories, or %NULL
*/
const char * const *
sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self)
char **
sysprof_elf_loader_dup_debug_dirs (SysprofElfLoader *self)
{
char **ret;
g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL);
return (const char * const *)self->debug_dirs;
g_rec_mutex_lock (&self->mutex);
ret = g_strdupv (self->debug_dirs);
g_rec_mutex_unlock (&self->mutex);
return ret;
}
/**
@ -197,12 +206,14 @@ sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self,
g_return_if_fail (SYSPROF_IS_ELF_LOADER (self));
g_return_if_fail (self->debug_dirs != NULL);
g_rec_mutex_lock (&self->mutex);
if (sysprof_set_strv (&self->debug_dirs, debug_dirs))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_DIRS]);
g_rec_mutex_unlock (&self->mutex);
}
/**
* sysprof_elf_loader_get_external_debug_dirs:
* sysprof_elf_loader_dup_external_debug_dirs:
* @self: a #SysprofElfLoader
*
* Gets the #SysprofElfLoader:external-debug-dirs property.
@ -210,14 +221,20 @@ sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self,
* See sysprof_elf_loader_set_external_debug_dirs() for how this
* property is used to locate ELF files.
*
* Returns: (nullable): an array of external debug directories, or %NULL
* Returns: (transfer full) (nullable): an array of external debug directories, or %NULL
*/
const char * const *
sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self)
char **
sysprof_elf_loader_dup_external_debug_dirs (SysprofElfLoader *self)
{
char **ret;
g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL);
return (const char * const *)self->external_debug_dirs;
g_rec_mutex_lock (&self->mutex);
ret = g_strdupv (self->external_debug_dirs);
g_rec_mutex_lock (&self->mutex);
return ret;
}
/**
@ -243,8 +260,10 @@ sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self,
{
g_return_if_fail (SYSPROF_IS_ELF_LOADER (self));
g_rec_mutex_lock (&self->mutex);
if (sysprof_set_strv (&self->external_debug_dirs, external_debug_dirs))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EXTERNAL_DEBUG_DIRS]);
g_rec_mutex_unlock (&self->mutex);
}
static char *
@ -414,6 +433,8 @@ sysprof_elf_loader_load (SysprofElfLoader *self,
g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL);
g_return_val_if_fail (!mount_namespace || SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace), NULL);
g_rec_mutex_lock (&self->mutex);
/* We must translate the file into a number of paths that may possibly
* locate the file in the case that there are overlays in the mount
* namespace. Each of the paths could be in a lower overlay layer.
@ -453,7 +474,10 @@ sysprof_elf_loader_load (SysprofElfLoader *self,
if (cached_elf != NULL)
{
if (sysprof_elf_matches (cached_elf, file_inode, build_id))
return g_object_ref (cached_elf);
{
g_rec_mutex_unlock (&self->mutex);
return g_object_ref (cached_elf);
}
}
continue;
@ -486,10 +510,15 @@ sysprof_elf_loader_load (SysprofElfLoader *self,
}
if (elf != NULL)
return g_steal_pointer (&elf);
{
g_rec_mutex_unlock (&self->mutex);
return g_steal_pointer (&elf);
}
}
failure:
g_rec_mutex_unlock (&self->mutex);
g_set_error_literal (error,
G_FILE_ERROR,
G_FILE_ERROR_NOENT,

View File

@ -199,11 +199,11 @@ sysprof_elf_symbolizer_get_property (GObject *object,
switch (prop_id)
{
case PROP_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_symbolizer_get_debug_dirs (self));
g_value_take_boxed (value, sysprof_elf_symbolizer_dup_debug_dirs (self));
break;
case PROP_EXTERNAL_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_symbolizer_get_external_debug_dirs (self));
g_value_take_boxed (value, sysprof_elf_symbolizer_dup_external_debug_dirs (self));
break;
default:
@ -277,12 +277,12 @@ sysprof_elf_symbolizer_new (void)
return g_object_new (SYSPROF_TYPE_ELF_SYMBOLIZER, NULL);
}
const char * const *
sysprof_elf_symbolizer_get_debug_dirs (SysprofElfSymbolizer *self)
char **
sysprof_elf_symbolizer_dup_debug_dirs (SysprofElfSymbolizer *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self), NULL);
return sysprof_elf_loader_get_debug_dirs (self->loader);
return sysprof_elf_loader_dup_debug_dirs (self->loader);
}
void
@ -294,12 +294,12 @@ sysprof_elf_symbolizer_set_debug_dirs (SysprofElfSymbolizer *self,
sysprof_elf_loader_set_debug_dirs (self->loader, debug_dirs);
}
const char * const *
sysprof_elf_symbolizer_get_external_debug_dirs (SysprofElfSymbolizer *self)
char **
sysprof_elf_symbolizer_dup_external_debug_dirs (SysprofElfSymbolizer *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self), NULL);
return sysprof_elf_loader_get_external_debug_dirs (self->loader);
return sysprof_elf_loader_dup_external_debug_dirs (self->loader);
}
void

View File

@ -37,12 +37,12 @@ GType sysprof_elf_symbolizer_get_type (void) G_GNUC
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolizer *sysprof_elf_symbolizer_new (void);
SYSPROF_AVAILABLE_IN_ALL
const char * const *sysprof_elf_symbolizer_get_debug_dirs (SysprofElfSymbolizer *self);
char **sysprof_elf_symbolizer_dup_debug_dirs (SysprofElfSymbolizer *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_elf_symbolizer_set_debug_dirs (SysprofElfSymbolizer *self,
const char * const *debug_dirs);
SYSPROF_AVAILABLE_IN_ALL
const char * const *sysprof_elf_symbolizer_get_external_debug_dirs (SysprofElfSymbolizer *self);
char **sysprof_elf_symbolizer_dup_external_debug_dirs (SysprofElfSymbolizer *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_elf_symbolizer_set_external_debug_dirs (SysprofElfSymbolizer *self,
const char * const *external_debug_dirs);

View File

@ -24,7 +24,7 @@
struct _SysprofStrings
{
GMutex mutex;
GRWLock rwlock;
GHashTable *hashtable;
};
@ -34,7 +34,7 @@ sysprof_strings_new (void)
SysprofStrings *self;
self = g_atomic_rc_box_new0 (SysprofStrings);
g_mutex_init (&self->mutex);
g_rw_lock_init (&self->rwlock);
self->hashtable = g_hash_table_new_full (g_str_hash,
g_str_equal,
(GDestroyNotify)g_ref_string_release,
@ -54,7 +54,7 @@ sysprof_strings_finalize (gpointer data)
{
SysprofStrings *self = data;
g_mutex_clear (&self->mutex);
g_rw_lock_clear (&self->rwlock);
g_clear_pointer (&self->hashtable, g_hash_table_unref);
}
@ -73,14 +73,24 @@ sysprof_strings_get (SysprofStrings *self,
if (string == NULL)
return NULL;
g_mutex_lock (&self->mutex);
g_rw_lock_reader_lock (&self->rwlock);
if (!(ret = g_hash_table_lookup (self->hashtable, string)))
{
ret = g_ref_string_new (string);
g_hash_table_insert (self->hashtable, ret, ret);
}
g_ref_string_acquire (ret);
g_mutex_unlock (&self->mutex);
g_rw_lock_reader_unlock (&self->rwlock);
g_rw_lock_writer_lock (&self->rwlock);
return ret;
/* Check again, now with write lock */
if (!(ret = g_hash_table_lookup (self->hashtable, string)))
{
ret = g_ref_string_new (string);
g_hash_table_insert (self->hashtable, ret, ret);
}
g_rw_lock_writer_unlock (&self->rwlock);
return g_ref_string_acquire (ret);
}
g_rw_lock_reader_unlock (&self->rwlock);
return g_ref_string_acquire (ret);
}