]> git.vpit.fr Git - perl/modules/indirect.git/commitdiff
Keep track of which thread-local contexts have been initialized
authorVincent Pit <vince@profvince.com>
Tue, 31 Mar 2015 19:15:40 +0000 (16:15 -0300)
committerVincent Pit <vince@profvince.com>
Tue, 31 Mar 2015 19:24:07 +0000 (16:24 -0300)
If you have two threads that are run in parallel, and the the module is
first loaded in one and then in the other, then our check functions might
be called in the second thread before the module's boot function is
executed. This is bad because the boot function is needed to initialize the
thread-local context, and this context is needed by the check functions.

To fix this, we keep track of which contexts were initialized thanks to a
refcounted pointer table.

indirect.xs

index d8e7d0deb9d21ebc25cbfa683a86c84e9db71af8..f020a1f52508ccdaebef759f8cfa086f160d4bbe 100644 (file)
@@ -191,6 +191,80 @@ static void indirect_ck_restore(pTHX_ OPCODE type, indirect_ck_t *old_ck_p) {
 
 /* --- Helpers ------------------------------------------------------------- */
 
+/* ... Check if the module is loaded ....................................... */
+
+#if I_THREADSAFE
+
+#define PTABLE_NAME        ptable_loaded
+#define PTABLE_VAL_FREE(V) NOOP
+
+#include "ptable.h"
+
+#define ptable_loaded_store(T, K, V) ptable_loaded_store(aPTBLMS_ (T), (K), (V))
+#define ptable_loaded_delete(T, K)   ptable_loaded_delete(aPTBLMS_ (T), (K))
+#define ptable_loaded_free(T)        ptable_loaded_free(aPTBLMS_ (T))
+
+#define indirect_loaded()
+
+static ptable *indirect_loaded_cxts          = NULL;
+static U32     indirect_loaded_cxts_refcount = 0;
+
+/* We must use preexistent global mutexes or we will never be able to destroy
+ * them. */
+#if I_HAS_PERL(5, 9, 3)
+# define I_LOADED_LOCK   MUTEX_LOCK(&PL_my_ctx_mutex)
+# define I_LOADED_UNLOCK MUTEX_UNLOCK(&PL_my_ctx_mutex)
+#else
+# define I_LOADED_LOCK   OP_REFCNT_LOCK
+# define I_LOADED_UNLOCK OP_REFCNT_UNLOCK
+#endif
+
+static int indirect_is_loaded(pTHX_ void *cxt) {
+#define indirect_is_loaded(C) indirect_is_loaded(aTHX_ (C))
+ int res = 0;
+
+ I_LOADED_LOCK;
+ if (indirect_loaded_cxts && ptable_fetch(indirect_loaded_cxts, cxt))
+  res = 1;
+ I_LOADED_UNLOCK;
+
+ return res;
+}
+
+static void indirect_set_loaded(pTHX_ void *cxt) {
+#define indirect_set_loaded(C) indirect_set_loaded(aTHX_ (C))
+ I_LOADED_LOCK;
+ if (!indirect_loaded_cxts) {
+  indirect_loaded_cxts          = ptable_new();
+  indirect_loaded_cxts_refcount = 0;
+ }
+ ++indirect_loaded_cxts_refcount;
+ ptable_loaded_store(indirect_loaded_cxts, cxt, cxt);
+ I_LOADED_UNLOCK;
+}
+
+static void indirect_clear_loaded(pTHX_ void *cxt) {
+#define indirect_clear_loaded(C) indirect_clear_loaded(aTHX_ (C))
+ I_LOADED_LOCK;
+ if (indirect_loaded_cxts_refcount <= 1) {
+  ptable_loaded_free(indirect_loaded_cxts);
+  indirect_loaded_cxts          = NULL;
+  indirect_loaded_cxts_refcount = 0;
+ } else {
+  --indirect_loaded_cxts_refcount;
+  ptable_loaded_delete(indirect_loaded_cxts, cxt);
+ }
+ I_LOADED_UNLOCK;
+}
+
+#else
+
+#define indirect_is_loaded(C)    (1)
+#define indirect_set_loaded(C)   NOOP
+#define indirect_clear_loaded(C) NOOP
+
+#endif
+
 /* ... Thread-safe hints ................................................... */
 
 #if I_WORKAROUND_REQUIRE_PROPAGATION
@@ -330,10 +404,14 @@ static void indirect_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
 static void indirect_thread_cleanup(pTHX_ void *ud) {
  dMY_CXT;
 
+ indirect_clear_loaded(&MY_CXT);
+
  SvREFCNT_dec(MY_CXT.global_code);
  MY_CXT.global_code = NULL;
+
  ptable_free(MY_CXT.map);
  MY_CXT.map = NULL;
+
  ptable_hints_free(MY_CXT.tbl);
  MY_CXT.tbl = NULL;
 }
@@ -504,11 +582,14 @@ static SV *indirect_hint(pTHX) {
  }
 #endif
 
- if (hint && SvIOK(hint))
+ if (hint && SvIOK(hint)) {
   return indirect_detag(hint);
- else {
else {
   dMY_CXT;
-  return MY_CXT.global_code;
+  if (indirect_is_loaded(&MY_CXT))
+   return MY_CXT.global_code;
+  else
+   return NULL;
  }
 }
 
@@ -564,7 +645,7 @@ static void indirect_map_delete(pTHX_ const OP *o) {
 #define indirect_map_delete(O) indirect_map_delete(aTHX_ (O))
  dMY_CXT;
 
- if (MY_CXT.map)
+ if (indirect_is_loaded(&MY_CXT) && MY_CXT.map)
   ptable_delete(MY_CXT.map, o);
 }
 
@@ -1008,6 +1089,8 @@ static void indirect_global_setup(pTHX) {
 static void indirect_local_teardown(pTHX_ void *param) {
  dMY_CXT;
 
+ indirect_clear_loaded(&MY_CXT);
+
  ptable_free(MY_CXT.map);
  MY_CXT.map = NULL;
 
@@ -1031,6 +1114,8 @@ static void indirect_local_setup(pTHX) {
  MY_CXT.map         = ptable_new();
  MY_CXT.global_code = NULL;
 
+ indirect_set_loaded(&MY_CXT);
+
  call_atexit(indirect_local_teardown, NULL);
 
  return;
@@ -1073,6 +1158,7 @@ PPCODE:
   MY_CXT.tbl         = t;
   MY_CXT.owner       = aTHX;
   MY_CXT.global_code = global_code_dup;
+  indirect_set_loaded(&MY_CXT);
  }
  gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
  if (gv) {