Keep track of which thread-local contexts have been initialized
[perl/modules/indirect.git] / indirect.xs
index d8e7d0d..f020a1f 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) {