Eina : now eina stringshare is thread safe if eina_threads_init() is called. Eina...
authorwatchwolf <watchwolf>
Sun, 1 Nov 2009 19:50:18 +0000 (19:50 +0000)
committerwatchwolf <watchwolf@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sun, 1 Nov 2009 19:50:18 +0000 (19:50 +0000)
git-svn-id: http://svn.enlightenment.org/svn/e/trunk/eina@43398 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/include/eina_main.h
src/include/eina_private.h
src/lib/eina_list.c
src/lib/eina_log.c
src/lib/eina_main.c
src/lib/eina_stringshare.c

index c504bbb..dc4f519 100644 (file)
@@ -35,6 +35,10 @@ EAPI int eina_init(void);
 
 EAPI int eina_shutdown(void);
 
+EAPI int eina_threads_init(void);
+
+EAPI int eina_threads_shutdown(void);
+
 /**
  * @}
  */
index ba4f018..e3ba452 100644 (file)
      }                                                 \
   } while(0);
 
+#ifdef EFL_HAVE_PTHREAD
+void eina_stringshare_threads_init(void);
+void eina_stringshare_threads_shutdown(void);
+void eina_log_threads_init(void);
+void eina_log_threads_shutdown(void);
+#endif
+
 #endif /* EINA_PRIVATE_H_ */
 
index 4185d9d..12aa0b6 100644 (file)
@@ -1,3 +1,5 @@
+// vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
+
 /* EINA - EFL data type library
  * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri, Tilman Sauerbeck,
  *                         Vincent Torri, Cedric Bail, Jorge Luis Zapata Muga,
@@ -1590,15 +1592,15 @@ eina_list_merge(Eina_List *left, Eina_List *right)
  * @param right The head of the new right list.
  * @return The new left list
  *
- * This function split @p list into two lists ( left and right ) after the node @p relative. @p Relative 
- * will become the last node of the left list. If @p list or @p right are NULL list is returns. 
+ * This function split @p list into two lists ( left and right ) after the node @p relative. @p Relative
+ * will become the last node of the left list. If @p list or @p right are NULL list is returns.
  * If @p relative is NULL right is set to @p list and NULL is returns.
  * If @p relative is the last node of @p list list is returns and @p right is set to NULL.
  *
  * list does not exist anymore after the split.
  *
  */
-EAPI Eina_List *
+   EAPI Eina_List *
 eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right)
 {
    Eina_List *next;
@@ -1610,12 +1612,12 @@ eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right)
    if (!list) return NULL;
    if (!relative)
      {
-         *right = list;
-         return NULL;
+       *right = list;
+       return NULL;
      }
    if (relative == eina_list_last(list)) return list;
 
-   next = eina_list_next(relative); 
+   next = eina_list_next(relative);
    next->prev = NULL;
    next->accounting = _eina_list_mempool_accounting_new(next);
    next->accounting->last = list->accounting->last;
@@ -1624,14 +1626,14 @@ eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right)
    itr = next;
    do
      {
-        itr->accounting = next->accounting;
-        next->accounting->count++;
-        itr = itr->next;
+       itr->accounting = next->accounting;
+       next->accounting->count++;
+       itr = itr->next;
      }
-       while (itr);
+   while (itr);
 
    relative->next = NULL;
-   list->accounting->last = relative; 
+   list->accounting->last = relative;
    list->accounting->count = list->accounting->count - next->accounting->count;
 
    return list;
index eb1c018..30c3be7 100644 (file)
@@ -376,6 +376,7 @@ static pthread_t _main_thread;
 #ifdef EINA_PTHREAD_SPIN
 static pthread_spinlock_t _log_lock;
 #define LOCK()                                                         \
+  if(_threads_enabled) \
   do {                                                                 \
      if (0)                                                            \
        fprintf(stderr, "+++LOG LOCKED!   [%s, %lu]\n",                 \
@@ -384,6 +385,7 @@ static pthread_spinlock_t _log_lock;
        pthread_spin_lock(&_log_lock);                                  \
   } while (0)
 #define UNLOCK()                                                       \
+  if(_threads_enabled) \
   do {                                                                 \
      if (EINA_UNLIKELY(_threads_enabled))                              \
        pthread_spin_unlock(&_log_lock);                                        \
@@ -396,8 +398,8 @@ static pthread_spinlock_t _log_lock;
 #define SHUTDOWN() pthread_spin_destroy(&_log_lock);
 #else
 static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
-#define LOCK() pthread_mutex_lock(&_log_mutex);
-#define UNLOCK() pthread_mutex_unlock(&_log_mutex);
+#define LOCK() if(_threads_enabled) pthread_mutex_lock(&_log_mutex);
+#define UNLOCK() if(_threads_enabled) pthread_mutex_unlock(&_log_mutex);
 #define INIT() do {} while (0)
 #define SHUTDOWN() do {} while (0)
 #endif
@@ -700,7 +702,6 @@ eina_log_print_prefix_update(void)
 #undef S
 }
 
-
 /*
  * Creates a colored domain name string.
  */
@@ -965,11 +966,6 @@ eina_log_init(void)
    assert((sizeof(_names)/sizeof(_names[0])) == EINA_LOG_LEVELS);
    assert((sizeof(_colors)/sizeof(_colors[0])) == EINA_LOG_LEVELS + 1);
 
-#ifdef EFL_HAVE_PTHREAD
-   _main_thread = pthread_self();
-   INIT();
-#endif
-
    // Check if color is disabled
    if ((tmp = getenv(EINA_LOG_ENV_COLOR_DISABLE)) && (atoi(tmp) == 1))
      _disable_color = EINA_TRUE;
@@ -1056,14 +1052,48 @@ eina_log_shutdown(void)
        free(tmp);
      }
 
+   return EINA_TRUE;
+}
+
+
 #ifdef EFL_HAVE_PTHREAD
-   SHUTDOWN();
-   _threads_enabled = 0;
-#endif
 
-   return EINA_TRUE;
+/**
+ * @internal
+ * @brief Activate the log mutex.
+ *
+ * This function activate the mutex in the eina log module. It is called by
+ * eina_thread_init().
+ *
+ * @see eina_thread_init()
+ */
+void 
+eina_log_threads_init(void)
+{
+   _main_thread = pthread_self();
+   _threads_enabled = EINA_TRUE;
+   INIT();
+}
+
+/**
+ * @internal
+ * @brief Shut down the log mutex.
+ *
+ * This function shuts down the mutex in the log module. 
+ * It is called by eina_thread_shutdown().
+ *
+ * @see eina_thread_shutdown()
+ */
+void
+eina_log_threads_shutdown(void)
+{
+    SHUTDOWN();
+    _threads_enabled = EINA_FALSE;
 }
 
+#endif
+
+
 /**
  * Enable logging module to handle threads.
  *
@@ -1471,3 +1501,4 @@ eina_log_vprint(int domain, Eina_Log_Level level, const char *file,
    eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args);
    UNLOCK();
 }
+
index a75631a..0b8af5e 100644 (file)
@@ -48,6 +48,7 @@
  */
 
 static int _eina_main_count = 0;
+static int _eina_main_thread_count = 0;
 static int _eina_log_dom = -1;
 
 #ifdef ERR
@@ -60,6 +61,18 @@ static int _eina_log_dom = -1;
 #endif
 #define DBG(...) EINA_LOG_DOM_DBG(_eina_log_dom, __VA_ARGS__)
 
+#ifdef EFL_HAVE_PTHREAD
+#include <pthread.h>
+static Eina_Bool _threads_activated = EINA_FALSE;
+static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK() if(_threads_activated) pthread_mutex_lock(&_mutex);
+#define UNLOCK() if(_threads_activated) pthread_mutex_unlock(&_mutex);
+#define UNLOCK_FORCE() pthread_mutex_unlock(&_mutex);
+#else
+#define LOCK() do {} while (0)
+#define UNLOCK() do {} while (0)
+#define UNLOCK_FORCE() do {} while (0)
+#endif
 
 /* place module init/shutdown functions here to avoid other modules
  * calling them by mistake.
@@ -220,6 +233,88 @@ eina_shutdown(void)
    return _eina_main_count;
 }
 
+
+/**
+ * @brief Initialize the mutexs of the Eina library.
+ *
+ * @return 1 or greater on success, 0 on error.
+ *
+ * This function sets up all the mutexs in all eina modules. It returns 0 on
+ * failure (that is, when one of the module fails to initialize),
+ * otherwise it returns the number of times it has already been
+ * called.
+ *
+ * When the mutexs are not used anymore, call eina_thread_shutdown() to shut down
+ * the mutexs.
+ */
+EAPI int
+eina_threads_init(void)
+{
+#ifdef EFL_HAVE_PTHREAD
+    int ret;
+    
+    LOCK();
+    ++_eina_main_thread_count;
+    ret = _eina_main_thread_count;
+
+    if(_eina_main_thread_count > 1) 
+    {
+        UNLOCK();
+        return ret;
+    }
+
+    eina_stringshare_threads_init();
+    eina_log_threads_init();
+    _threads_activated = EINA_TRUE;
+
+    return ret;
+#else
+    return 0;
+#endif
+}
+
+/**
+ * @brief Shut down mutexs in the Eina library.
+ *
+ * @return 0 when all mutexs are completely shut down, 1 or
+ * greater otherwise.
+ *
+ * This function shuts down the mutexs in the Eina library. It returns 0 when it has
+ * been called the same number of times than eina_thread_init(). In that case
+ * it shut down all the mutexs.
+ *
+ * Once this function succeeds (that is, @c 0 is returned), you must
+ * not call any of the Eina function in a thread anymore. You must call
+ * eina_thread_init() again to use the Eina functions in a thread again.
+ */
+EAPI int
+eina_threads_shutdown(void)
+{
+#ifdef EFL_HAVE_PTHREAD
+    int ret;
+
+    LOCK();
+    ret = --_eina_main_thread_count;
+    if(_eina_main_thread_count > 0) 
+    {
+        UNLOCK();
+        return ret; 
+    }
+
+    eina_stringshare_threads_shutdown();
+    eina_log_threads_shutdown();
+
+    _threads_activated = EINA_FALSE;
+
+    UNLOCK_FORCE();
+
+    return ret;
+#else
+    return 0;
+#endif
+}
+
+
 /**
  * @}
  */
index 50c3f86..8e445b6 100644 (file)
@@ -100,19 +100,23 @@ static const char EINA_MAGIC_STRINGSHARE_HEAD_STR[] = "Eina Stringshare Head";
 static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node";
 
 
-#define EINA_MAGIC_CHECK_STRINGSHARE_HEAD(d, ...)              \
+#define EINA_MAGIC_CHECK_STRINGSHARE_HEAD(d, unlock, ...)      \
   do {                                                         \
     if (!EINA_MAGIC_CHECK((d), EINA_MAGIC_STRINGSHARE_HEAD))   \
     {                                                          \
         EINA_MAGIC_FAIL((d), EINA_MAGIC_STRINGSHARE_HEAD);     \
+        unlock;                                                        \
         return __VA_ARGS__;                                    \
     }                                                          \
   } while (0);
 
-#define EINA_MAGIC_CHECK_STRINGSHARE_NODE(d)                   \
+#define EINA_MAGIC_CHECK_STRINGSHARE_NODE(d, unlock)           \
   do {                                                         \
     if (!EINA_MAGIC_CHECK((d), EINA_MAGIC_STRINGSHARE_NODE))   \
+    {                                                          \
+      unlock;                                                  \
       EINA_MAGIC_FAIL((d), EINA_MAGIC_STRINGSHARE_NODE);       \
+    }                                                          \
   } while (0);
 
 typedef struct _Eina_Stringshare             Eina_Stringshare;
@@ -170,6 +174,27 @@ static int _eina_stringshare_log_dom = -1;
 #endif
 #define DBG(...) EINA_LOG_DOM_DBG(_eina_stringshare_log_dom, __VA_ARGS__)
 
+
+
+#ifdef EFL_HAVE_PTHREAD
+#include <pthread.h>
+static Eina_Bool _threads_activated = EINA_FALSE;
+//string < 4
+static pthread_mutex_t _mutex_small = PTHREAD_MUTEX_INITIALIZER;
+//string >= 4
+static pthread_mutex_t _mutex_big = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK_SMALL() if(_threads_activated) pthread_mutex_lock(&_mutex_small);
+#define UNLOCK_SMALL() if(_threads_activated) pthread_mutex_unlock(&_mutex_small);
+#define LOCK_BIG() if(_threads_activated) pthread_mutex_lock(&_mutex_big); 
+#define UNLOCK_BIG() if(_threads_activated) pthread_mutex_unlock(&_mutex_big);
+#else
+#define LOCK_SMALL() do {} while (0)
+#define UNLOCK_SMALL() do {} while (0)
+#define LOCK_BIG() do {} while (0)
+#define UNLOCK_BIG() do {} while (0)
+#endif
+
+
 static const unsigned char _eina_stringshare_single[512] = {
   0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,
   16,0,17,0,18,0,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,
@@ -276,6 +301,9 @@ _eina_stringshare_population_stats(void)
 static void
 _eina_stringshare_population_add(int slen)
 {
+   LOCK_SMALL();
+   LOCK_BIG();
+
    population.count++;
    if (population.count > population.max)
      population.max = population.count;
@@ -286,14 +314,23 @@ _eina_stringshare_population_add(int slen)
        if (population_group[slen].count > population_group[slen].max)
          population_group[slen].max = population_group[slen].count;
      }
+
+   UNLOCK_BIG();
+   UNLOCK_SMALL();
 }
 
 static void
 _eina_stringshare_population_del(int slen)
 {
+   LOCK_SMALL();
+   LOCK_BIG();
+
    population.count--;
    if (slen < 4)
      population_group[slen].count--;
+
+   UNLOCK_BIG();
+   UNLOCK_SMALL();
 }
 
 static void
@@ -331,7 +368,7 @@ static void _eina_stringshare_population_head_del(__UNUSED__ Eina_Stringshare_He
 static int
 _eina_stringshare_cmp(const Eina_Stringshare_Head *ed, const int *hash, __UNUSED__ int length, __UNUSED__ void *data)
 {
-   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed, 0);
+   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed, 0);
 
    return ed->hash - *hash;
 }
@@ -339,8 +376,8 @@ _eina_stringshare_cmp(const Eina_Stringshare_Head *ed, const int *hash, __UNUSED
 static Eina_Rbtree_Direction
 _eina_stringshare_node(const Eina_Stringshare_Head *left, const Eina_Stringshare_Head *right, __UNUSED__ void *data)
 {
-   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(left, 0);
-   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(right, 0);
+   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(left, 0);
+   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(right, 0);
 
    if (left->hash - right->hash < 0)
      return EINA_RBTREE_LEFT;
@@ -350,7 +387,7 @@ _eina_stringshare_node(const Eina_Stringshare_Head *left, const Eina_Stringshare
 static void
 _eina_stringshare_head_free(Eina_Stringshare_Head *ed, __UNUSED__ void *data)
 {
-   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed);
+   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed);
 
    while (ed->head)
      {
@@ -879,6 +916,9 @@ eina_stringshare_shutdown(void)
 {
    unsigned int i;
 
+   LOCK_SMALL();
+   LOCK_BIG();
+
    _eina_stringshare_population_stats();
 
    /* remove any string still in the table */
@@ -893,9 +933,48 @@ eina_stringshare_shutdown(void)
    _eina_stringshare_small_shutdown();
    eina_log_domain_unregister(_eina_stringshare_log_dom);
    _eina_stringshare_log_dom = -1;
+
+   UNLOCK_BIG();
+   UNLOCK_SMALL();
+
+
    return EINA_TRUE;
 }
 
+#ifdef EFL_HAVE_PTHREAD
+
+/**
+ * @internal
+ * @brief Activate the stringshare mutexs.
+ *
+ * This function activate the mutexs in the eina stringshare module. It is called by
+ * eina_thread_init().
+ *
+ * @see eina_thread_init()
+ */
+void 
+eina_stringshare_threads_init(void)
+{
+   _threads_activated = EINA_TRUE; 
+}
+
+/**
+ * @internal
+ * @brief Shut down the stringshare mutexs.
+ *
+ * This function shuts down the mutexs in the stringshare module. 
+ * It is called by eina_thread_shutdown().
+ *
+ * @see eina_thread_shutdown()
+ */
+void
+eina_stringshare_threads_shutdown(void)
+{
+   _threads_activated = EINA_FALSE; 
+}
+
+#endif
+
 /**
  * @brief Retrieve an instance of a string for use in a program.
  *
@@ -936,36 +1015,53 @@ eina_stringshare_add_length(const char *str, unsigned int slen)
    else if (slen == 1)
      return (const char *)_eina_stringshare_single + ((*str) << 1);
    else if (slen < 4)
-     return _eina_stringshare_small_add(str, slen);
+     {
+       LOCK_SMALL();
+       const char *s = _eina_stringshare_small_add(str, slen);
+       UNLOCK_SMALL();
+       return s;
+     }
 
    hash = eina_hash_superfast(str, slen);
    hash_num = hash & 0xFF;
    hash = (hash >> 8) & EINA_STRINGSHARE_MASK;
 
+   LOCK_BIG();
    p_bucket = share->buckets + hash_num;
+
    ed = _eina_stringshare_find_hash(*p_bucket, hash);
    if (!ed)
-     return _eina_stringshare_add_head(p_bucket, hash, str, slen);
+     {
+       const char *s =  _eina_stringshare_add_head(p_bucket, hash, str, slen);
+       UNLOCK_BIG();
+       return s;
+     }
 
-   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed, NULL);
+   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed, UNLOCK_BIG(), NULL);
 
    el = _eina_stringshare_head_find(ed, str, slen);
    if (el)
      {
-       EINA_MAGIC_CHECK_STRINGSHARE_NODE(el);
+       EINA_MAGIC_CHECK_STRINGSHARE_NODE(el, UNLOCK_BIG());
        el->references++;
+       UNLOCK_BIG();
        return el->str;
      }
 
    el = _eina_stringshare_node_alloc(slen);
    if (!el)
-     return NULL;
+     {
+       UNLOCK_BIG();
+       return NULL;
+     }
 
    _eina_stringshare_node_init(el, str, slen);
    el->next = ed->head;
    ed->head = el;
    _eina_stringshare_population_head_add(ed);
 
+   UNLOCK_BIG();
+
    return el->str;
 }
 
@@ -1014,7 +1110,7 @@ _eina_stringshare_node_from_str(const char *str)
    const size_t offset = (char *)&(t.str) - (char *)&t;
 
    node = (Eina_Stringshare_Node *)(str - offset);
-   EINA_MAGIC_CHECK_STRINGSHARE_NODE(node);
+   EINA_MAGIC_CHECK_STRINGSHARE_NODE(node);
    return node;
 }
 
@@ -1047,18 +1143,27 @@ eina_stringshare_ref(const char *str)
    if (slen < 2)
      {
        _eina_stringshare_population_add(slen);
+
        return str;
      }
    else if (slen < 4)
      {
        _eina_stringshare_population_add(slen);
-       return _eina_stringshare_small_add(str, slen);
+
+       LOCK_SMALL();
+       const char *s =  _eina_stringshare_small_add(str, slen);
+       UNLOCK_SMALL();
+
+       return s;
      }
 
+   LOCK_BIG();
    node = _eina_stringshare_node_from_str(str);
    node->references++;
    DBG("str=%p (%s) refs=%u", str, str, node->references);
 
+   UNLOCK_BIG();
+
    _eina_stringshare_population_add(node->length);
 
    return str;
@@ -1102,15 +1207,20 @@ eina_stringshare_del(const char *str)
      return;
    else if (slen < 4)
      {
+       LOCK_SMALL();
        _eina_stringshare_small_del(str, slen);
+       UNLOCK_SMALL();
        return;
      }
 
+   LOCK_BIG();
+
    node = _eina_stringshare_node_from_str(str);
    if (node->references > 1)
      {
        node->references--;
        DBG("str=%p (%s) refs=%u", str, str, node->references);
+       UNLOCK_BIG();
        return;
      }
 
@@ -1127,7 +1237,7 @@ eina_stringshare_del(const char *str)
    if (!ed)
      goto on_error;
 
-   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed);
+   EINA_MAGIC_CHECK_STRINGSHARE_HEAD(ed, UNLOCK_BIG());
 
    if (!_eina_stringshare_head_remove_node(ed, node))
      goto on_error;
@@ -1140,9 +1250,12 @@ eina_stringshare_del(const char *str)
    else
      _eina_stringshare_population_head_del(ed);
 
+   UNLOCK_BIG();
+
    return;
 
  on_error:
+   UNLOCK_BIG();
    /* possible segfault happened before here, but... */
    CRITICAL("EEEK trying to del non-shared stringshare \"%s\"", str);
 }
@@ -1231,7 +1344,10 @@ static Eina_Bool
 eina_iterator_array_check(const Eina_Rbtree *rbtree __UNUSED__, Eina_Stringshare_Head *head, struct dumpinfo *fdata)
 {
    Eina_Stringshare_Node *node;
-   
+
+   LOCK_SMALL();
+   LOCK_BIG();
+
    fdata->used += sizeof(Eina_Stringshare_Head);
    for (node = head->head; node; node = node->next)
      {
@@ -1243,6 +1359,10 @@ eina_iterator_array_check(const Eina_Rbtree *rbtree __UNUSED__, Eina_Stringshare
        fdata->dups += node->references - 1;
        fdata->unique++;
      }
+
+   UNLOCK_BIG();
+   UNLOCK_SMALL();
+
    return EINA_TRUE;
 }
 
@@ -1266,7 +1386,12 @@ eina_stringshare_dump(void)
    di.unique = 0;
    printf("DDD:   len   ref string\n");
    printf("DDD:-------------------\n");
+
+   LOCK_SMALL();
    _eina_stringshare_small_dump(&di);
+   UNLOCK_SMALL();
+
+   LOCK_BIG();
    for (i = 0; i < EINA_STRINGSHARE_BUCKETS; i++)
      {
        if (!share->buckets[i]) continue;
@@ -1294,8 +1419,11 @@ eina_stringshare_dump(void)
    for (i = 0; i < sizeof (population_group) / sizeof (population_group[0]); ++i)
      fprintf(stderr, "DDD: %i strings of length %i, max strings: %i\n", population_group[i].count, i, population_group[i].max);
 #endif
+
+   UNLOCK_BIG();
 }
 
 /**
  * @}
  */
+