Add new thread creation API
[platform/upstream/glib.git] / glib / gthread.c
index 9b2576d..49e3060 100644 (file)
@@ -44,8 +44,7 @@
 #include "gthreadprivate.h"
 #include "deprecated/gthread.h"
 
-#include "gslice.h"
-#include "gmain.h"
+#include <string.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #include <windows.h>
 #endif /* G_OS_WIN32 */
 
-#include <string.h>
-
 #include "garray.h"
-#include "gbitlock.h"
+#include "gslice.h"
 #include "gslist.h"
 #include "gtestutils.h"
-#include "gtimer.h"
 
 /**
  * SECTION:threads
  * Since: 2.32
  */
 
-/* GPrivate Documentation {{{1 --------------------------------------- */
-
-/**
- * GPrivate:
- *
- * <note><para>
- * #GStaticPrivate is a better choice for most uses.
- * </para></note>
- *
- * The #GPrivate struct is an opaque data structure to represent a
- * thread private data key. Threads can thereby obtain and set a
- * pointer which is private to the current thread. Take our
- * <function>give_me_next_number(<!-- -->)</function> example from
- * above.  Suppose we don't want <literal>current_number</literal> to be
- * shared between the threads, but instead to be private to each thread.
- * This can be done as follows:
- *
- * <example>
- *  <title>Using GPrivate for per-thread data</title>
- *  <programlisting>
- *   GPrivate* current_number_key = NULL; /<!-- -->* Must be initialized somewhere
- *                                           with g_private_new (g_free); *<!-- -->/
- *
- *   int
- *   give_me_next_number (void)
- *   {
- *     int *current_number = g_private_get (current_number_key);
- *
- *     if (!current_number)
- *       {
- *         current_number = g_new (int, 1);
- *         *current_number = 0;
- *         g_private_set (current_number_key, current_number);
- *       }
- *
- *     *current_number = calc_next_number (*current_number);
- *
- *     return *current_number;
- *   }
- *  </programlisting>
- * </example>
- *
- * Here the pointer belonging to the key
- * <literal>current_number_key</literal> is read. If it is %NULL, it has
- * not been set yet. Then get memory for an integer value, assign this
- * memory to the pointer and write the pointer back. Now we have an
- * integer value that is private to the current thread.
- *
- * The #GPrivate struct should only be accessed via the
- * <function>g_private_</function> functions.
- */
-
 /* GThread Documentation {{{1 ---------------------------------------- */
 
 /**
@@ -638,32 +582,28 @@ typedef struct _GRealThread GRealThread;
 struct  _GRealThread
 {
   GThread thread;
-  /* Bit 0 protects private_data. To avoid deadlocks,
-   * do not block while holding this (particularly on
-   * the g_thread lock).
-   */
-  volatile gint private_data_lock;
   GArray *private_data;
   GRealThread *next;
+  const gchar *name;
   gpointer retval;
   GSystemThread system_thread;
 };
 
-#define LOCK_PRIVATE_DATA(self)   g_bit_lock (&(self)->private_data_lock, 0)
-#define UNLOCK_PRIVATE_DATA(self) g_bit_unlock (&(self)->private_data_lock, 0)
-
 /* Local Data {{{1 -------------------------------------------------------- */
 
 gboolean         g_threads_got_initialized = FALSE;
 GSystemThread    zero_thread; /* This is initialized to all zero */
-GMutex           g_once_mutex = G_MUTEX_INIT;
 
+GMutex           g_once_mutex = G_MUTEX_INIT;
 static GCond     g_once_cond = G_COND_INIT;
-static GPrivate  g_thread_specific_private;
+static GSList   *g_once_init_list = NULL;
+
+static void g_thread_cleanup (gpointer data);
+static GPrivate     g_thread_specific_private = G_PRIVATE_INIT (g_thread_cleanup);
 static GRealThread *g_thread_all_threads = NULL;
-static GSList   *g_thread_free_indices = NULL;
-static GSList*   g_once_init_list = NULL;
+static GSList      *g_thread_free_indices = NULL;
 
+/* Protects g_thread_all_threads and g_thread_free_indices */
 G_LOCK_DEFINE_STATIC (g_thread);
 
 /* Initialisation {{{1 ---------------------------------------------------- */
@@ -694,8 +634,6 @@ G_LOCK_DEFINE_STATIC (g_thread);
  * having to link with the thread libraries.</para></note>
  */
 
-static void g_thread_cleanup (gpointer data);
-
 void
 g_thread_init_glib (void)
 {
@@ -714,7 +652,6 @@ g_thread_init_glib (void)
 
   /* setup the basic threading system */
   g_threads_got_initialized = TRUE;
-  g_private_init (&g_thread_specific_private, g_thread_cleanup);
   g_private_set (&g_thread_specific_private, main_thread);
   g_system_thread_self (&main_thread->system_thread);
 
@@ -926,8 +863,9 @@ g_once_init_leave (volatile gsize *value_location,
 typedef struct _GStaticPrivateNode GStaticPrivateNode;
 struct _GStaticPrivateNode
 {
-  gpointer       data;
-  GDestroyNotify destroy;
+  gpointer        data;
+  GDestroyNotify  destroy;
+  GStaticPrivate *owner;
 };
 
 /**
@@ -1005,15 +943,31 @@ g_static_private_get (GStaticPrivate *private_key)
   GArray *array;
   gpointer ret = NULL;
 
-  LOCK_PRIVATE_DATA (self);
-
   array = self->private_data;
 
   if (array && private_key->index != 0 && private_key->index <= array->len)
-    ret = g_array_index (array, GStaticPrivateNode,
-                         private_key->index - 1).data;
+    {
+      GStaticPrivateNode *node;
+
+      node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
+
+      /* Deal with the possibility that the GStaticPrivate which used
+       * to have this index got freed and the index got allocated to
+       * a new one. In this case, the data in the node is stale, so
+       * free it and return NULL.
+       */
+      if (G_UNLIKELY (node->owner != private_key))
+        {
+          if (node->destroy)
+            node->destroy (node->data);
+          node->destroy = NULL;
+          node->data = NULL;
+          node->owner = NULL;
+        }
+
+      ret = node->data;
+    }
 
-  UNLOCK_PRIVATE_DATA (self);
   return ret;
 }
 
@@ -1039,39 +993,33 @@ g_static_private_get (GStaticPrivate *private_key)
  */
 void
 g_static_private_set (GStaticPrivate *private_key,
-                     gpointer        data,
-                     GDestroyNotify  notify)
+                      gpointer        data,
+                      GDestroyNotify  notify)
 {
   GRealThread *self = (GRealThread*) g_thread_self ();
   GArray *array;
   static guint next_index = 0;
   GStaticPrivateNode *node;
-  gpointer ddata = NULL;
-  GDestroyNotify ddestroy = NULL;
 
   if (!private_key->index)
     {
       G_LOCK (g_thread);
 
       if (!private_key->index)
-       {
-         if (g_thread_free_indices)
-           {
-             private_key->index =
-               GPOINTER_TO_UINT (g_thread_free_indices->data);
-             g_thread_free_indices =
-               g_slist_delete_link (g_thread_free_indices,
-                                    g_thread_free_indices);
-           }
-         else
-           private_key->index = ++next_index;
-       }
+        {
+          if (g_thread_free_indices)
+            {
+              private_key->index = GPOINTER_TO_UINT (g_thread_free_indices->data);
+              g_thread_free_indices = g_slist_delete_link (g_thread_free_indices,
+                                                           g_thread_free_indices);
+            }
+          else
+            private_key->index = ++next_index;
+        }
 
       G_UNLOCK (g_thread);
     }
 
-  LOCK_PRIVATE_DATA (self);
-
   array = self->private_data;
   if (!array)
     {
@@ -1084,16 +1032,12 @@ g_static_private_set (GStaticPrivate *private_key,
 
   node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
 
-  ddata = node->data;
-  ddestroy = node->destroy;
+  if (node->destroy)
+    node->destroy (node->data);
 
   node->data = data;
   node->destroy = notify;
-
-  UNLOCK_PRIVATE_DATA (self);
-
-  if (ddestroy)
-    ddestroy (ddata);
+  node->owner = private_key;
 }
 
 /**
@@ -1111,76 +1055,20 @@ void
 g_static_private_free (GStaticPrivate *private_key)
 {
   guint idx = private_key->index;
-  GRealThread *thread, *next;
-  GArray *garbage = NULL;
 
   if (!idx)
     return;
 
   private_key->index = 0;
 
+  /* Freeing the per-thread data is deferred to either the
+   * thread end or the next g_static_private_get() call for
+   * the same index.
+   */
   G_LOCK (g_thread);
-
-  thread = g_thread_all_threads;
-
-  for (thread = g_thread_all_threads; thread; thread = next)
-    {
-      GArray *array;
-
-      next = thread->next;
-
-      LOCK_PRIVATE_DATA (thread);
-
-      array = thread->private_data;
-
-      if (array && idx <= array->len)
-       {
-         GStaticPrivateNode *node = &g_array_index (array,
-                                                    GStaticPrivateNode,
-                                                    idx - 1);
-         gpointer ddata = node->data;
-         GDestroyNotify ddestroy = node->destroy;
-
-         node->data = NULL;
-         node->destroy = NULL;
-
-          if (ddestroy)
-            {
-              /* defer non-trivial destruction til after we've finished
-               * iterating, since we must continue to hold the lock */
-              if (garbage == NULL)
-                garbage = g_array_new (FALSE, TRUE,
-                                       sizeof (GStaticPrivateNode));
-
-              g_array_set_size (garbage, garbage->len + 1);
-
-              node = &g_array_index (garbage, GStaticPrivateNode,
-                                     garbage->len - 1);
-              node->data = ddata;
-              node->destroy = ddestroy;
-            }
-       }
-
-      UNLOCK_PRIVATE_DATA (thread);
-    }
   g_thread_free_indices = g_slist_prepend (g_thread_free_indices,
-                                          GUINT_TO_POINTER (idx));
+                                           GUINT_TO_POINTER (idx));
   G_UNLOCK (g_thread);
-
-  if (garbage)
-    {
-      guint i;
-
-      for (i = 0; i < garbage->len; i++)
-        {
-          GStaticPrivateNode *node;
-
-          node = &g_array_index (garbage, GStaticPrivateNode, i);
-          node->destroy (node->data);
-        }
-
-      g_array_free (garbage, TRUE);
-    }
 }
 
 /* GThread {{{1 -------------------------------------------------------- */
@@ -1193,50 +1081,46 @@ g_thread_cleanup (gpointer data)
       GRealThread* thread = data;
       GArray *array;
 
-      LOCK_PRIVATE_DATA (thread);
       array = thread->private_data;
       thread->private_data = NULL;
-      UNLOCK_PRIVATE_DATA (thread);
 
       if (array)
-       {
-         guint i;
-
-         for (i = 0; i < array->len; i++ )
-           {
-             GStaticPrivateNode *node =
-               &g_array_index (array, GStaticPrivateNode, i);
-             if (node->destroy)
-               node->destroy (node->data);
-           }
-         g_array_free (array, TRUE);
-       }
+        {
+          guint i;
+
+          for (i = 0; i < array->len; i++ )
+            {
+              GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i);
+              if (node->destroy)
+                node->destroy (node->data);
+            }
+          g_array_free (array, TRUE);
+        }
 
       /* We only free the thread structure if it isn't joinable.
        * If it is, the structure is freed in g_thread_join()
        */
       if (!thread->thread.joinable)
-       {
-         GRealThread *t, *p;
-
-         G_LOCK (g_thread);
-         for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
-           {
-             if (t == thread)
-               {
-                 if (p)
-                   p->next = t->next;
-                 else
-                   g_thread_all_threads = t->next;
-                 break;
-               }
-           }
-         G_UNLOCK (g_thread);
-
-         /* Just to make sure, this isn't used any more */
-         g_system_thread_assign (thread->system_thread, zero_thread);
+        {
+          GRealThread *t, *p;
+
+          G_LOCK (g_thread);
+          for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
+            {
+              if (t == thread)
+                {
+                  if (p)
+                    p->next = t->next;
+                  else
+                    g_thread_all_threads = t->next;
+                  break;
+                }
+            }
+          G_UNLOCK (g_thread);
+          /* Just to make sure, this isn't used any more */
+          g_system_thread_assign (thread->system_thread, zero_thread);
           g_free (thread);
-       }
+        }
     }
 }
 
@@ -1262,14 +1146,19 @@ g_thread_create_proxy (gpointer data)
 }
 
 /**
- * g_thread_create:
+ * g_thread_new:
+ * @name: a name for the new thread
  * @func: a function to execute in the new thread
  * @data: an argument to supply to the new thread
  * @joinable: should this thread be joinable?
- * @error: return location for error, or %NULL
+ * @error: return location for error
  *
  * This function creates a new thread.
  *
+ * The @name can be useful for discriminating threads in
+ * a debugger. Some systems restrict the length of @name to
+ * 16 bytes.
+ *
  * If @joinable is %TRUE, you can wait for this threads termination
  * calling g_thread_join(). Otherwise the thread will just disappear
  * when it terminates.
@@ -1281,28 +1170,37 @@ g_thread_create_proxy (gpointer data)
  * The error is set, if and only if the function returns %NULL.
  *
  * Returns: the new #GThread on success
+ *
+ * Since: 2.32
  */
 GThread *
-g_thread_create (GThreadFunc   func,
-                 gpointer      data,
-                 gboolean      joinable,
-                 GError      **error)
+g_thread_new (const gchar  *name,
+              GThreadFunc   func,
+              gpointer      data,
+              gboolean      joinable,
+              GError      **error)
 {
-  return g_thread_create_with_stack_size (func, data, joinable, 0, error);
+  return g_thread_new_full (name, func, data, joinable, 0, error);
 }
 
 /**
- * g_thread_create_with_stack_size:
+ * g_thread_new_full:
+ * @name: a name for the new thread
  * @func: a function to execute in the new thread
  * @data: an argument to supply to the new thread
  * @joinable: should this thread be joinable?
  * @stack_size: a stack size for the new thread
  * @error: return location for error
  *
- * This function creates a new thread. If the underlying thread
- * implementation supports it, the thread gets a stack size of
- * @stack_size or the default value for the current platform, if
- * @stack_size is 0.
+ * This function creates a new thread.
+ *
+ * The @name can be useful for discriminating threads in
+ * a debugger. Some systems restrict the length of @name to
+ * 16 bytes.
+ *
+ * If the underlying thread implementation supports it, the thread
+ * gets a stack size of @stack_size or the default value for the
+ * current platform, if @stack_size is 0.
  *
  * If @joinable is %TRUE, you can wait for this threads termination
  * calling g_thread_join(). Otherwise the thread will just disappear
@@ -1314,8 +1212,8 @@ g_thread_create (GThreadFunc   func,
  * @error can be %NULL to ignore errors, or non-%NULL to report errors.
  * The error is set, if and only if the function returns %NULL.
  *
- * <note><para>Only use g_thread_create_with_stack_size() if you
- * really can't use g_thread_create() instead. g_thread_create()
+ * <note><para>Only use a non-zero @stack_size if you
+ * really can't use the default instead. g_thread_new()
  * does not take @stack_size, as it should only be used in cases
  * in which it is unavoidable.</para></note>
  *
@@ -1323,12 +1221,13 @@ g_thread_create (GThreadFunc   func,
  *
  * Since: 2.32
  */
-GThread*
-g_thread_create_with_stack_size (GThreadFunc   func,
-                                 gpointer      data,
-                                 gboolean      joinable,
-                                 gsize         stack_size,
-                                 GError      **error)
+GThread *
+g_thread_new_full (const gchar  *name,
+                   GThreadFunc   func,
+                   gpointer      data,
+                   gboolean      joinable,
+                   gsize         stack_size,
+                   GError      **error)
 {
   GRealThread* result;
   GError *local_error = NULL;
@@ -1340,6 +1239,7 @@ g_thread_create_with_stack_size (GThreadFunc   func,
   result->thread.func = func;
   result->thread.data = data;
   result->private_data = NULL;
+  result->name = name;
   G_LOCK (g_thread);
   g_system_thread_create (g_thread_create_proxy, result,
                           stack_size, joinable,
@@ -1423,16 +1323,15 @@ g_thread_join (GThread* thread)
   for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
     {
       if (t == (GRealThread*) thread)
-       {
-         if (p)
-           p->next = t->next;
-         else
-           g_thread_all_threads = t->next;
-         break;
-       }
+        {
+          if (p)
+            p->next = t->next;
+          else
+            g_thread_all_threads = t->next;
+          break;
+        }
     }
   G_UNLOCK (g_thread);
-
   /* Just to make sure, this isn't used any more */
   thread->joinable = 0;
   g_system_thread_assign (real->system_thread, zero_thread);
@@ -1599,43 +1498,5 @@ g_cond_free (GCond *cond)
   g_slice_free (GCond, cond);
 }
 
-/* GPrivate {{{1 ------------------------------------------------------ */
-
-/**
- * g_private_new:
- * @destructor: a function to destroy the data keyed to
- *     the #GPrivate when a thread ends
- *
- * Creates a new #GPrivate. If @destructor is non-%NULL, it is a
- * pointer to a destructor function. Whenever a thread ends and the
- * corresponding pointer keyed to this instance of #GPrivate is
- * non-%NULL, the destructor is called with this pointer as the
- * argument.
- *
- * <note><para>
- * #GStaticPrivate is a better choice for most uses.
- * </para></note>
- *
- * <note><para>@destructor is used quite differently from @notify in
- * g_static_private_set().</para></note>
- *
- * <note><para>A #GPrivate cannot be freed. Reuse it instead, if you
- * can, to avoid shortage, or use #GStaticPrivate.</para></note>
- *
- * <note><para>This function will abort if g_thread_init() has not been
- * called yet.</para></note>
- *
- * Returns: a newly allocated #GPrivate
- */
-GPrivate *
-g_private_new (GDestroyNotify notify)
-{
-  GPrivate *key;
-
-  key = g_slice_new (GPrivate);
-  g_private_init (key, notify);
-
-  return key;
-}
-
- /* vim: set foldmethod=marker: */
+/* Epilogue {{{1 */
+/* vim: set foldmethod=marker: */