]> git.vpit.fr Git - perl/modules/re-engine-Hooks.git/blobdiff - Hooks.xs
Attach the callbacks to every regexps in a thread-safe way
[perl/modules/re-engine-Hooks.git] / Hooks.xs
index ef0c5082bdecc69867f59c85ad19932c0158edc9..b312223c08fff684b75445b2a1940a2731d85af7 100644 (file)
--- a/Hooks.xs
+++ b/Hooks.xs
 # define SvPV_const(S, L) SvPV(S, L)
 #endif
 
+/* ... Thread safety and multiplicity ...................................... */
+
+#ifndef REH_MULTIPLICITY
+# if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
+#  define REH_MULTIPLICITY 1
+# else
+#  define REH_MULTIPLICITY 0
+# endif
+#endif
+
 #ifdef USE_ITHREADS
 # define REH_LOCK(M)   MUTEX_LOCK(M)
 # define REH_UNLOCK(M) MUTEX_UNLOCK(M)
@@ -98,54 +108,6 @@ void reh_register(pTHX_ const char *key, reh_config *cfg) {
  return;
 }
 
-/* --- Private API --------------------------------------------------------- */
-
-void reh_call_comp_hook(pTHX_ regexp *rx, regnode *node) {
- SV *hint = reh_hint();
-
- if (hint && SvPOK(hint)) {
-  STRLEN      len;
-  const char *keys = SvPV_const(hint, len);
-  reh_action *a;
-
-  REH_LOCK(&reh_action_list_mutex);
-  a = reh_action_list;
-  REH_UNLOCK(&reh_action_list_mutex);
-
-  for (; a; a = a->next) {
-   if (a->cbs.comp) {
-    char *p = strstr(keys, a->key);
-
-    if (p && (p + a->klen <= keys + len) && p[a->klen] == ' ')
-     a->cbs.comp(aTHX_ rx, node);
-   }
-  }
- }
-}
-
-void reh_call_exec_hook(pTHX_ regexp *rx, regnode *node, regmatch_info *reginfo, regmatch_state *st) {
- SV *hint = reh_hint();
-
- if (hint && SvPOK(hint)) {
-  STRLEN      len;
-  const char *keys = SvPV_const(hint, len);
-  reh_action *a;
-
-  REH_LOCK(&reh_action_list_mutex);
-  a = reh_action_list;
-  REH_UNLOCK(&reh_action_list_mutex);
-
-  for (; a; a = a->next) {
-   if (a->cbs.exec) {
-    char *p = strstr(keys, a->key);
-
-    if (p && (p + a->klen <= keys + len) && p[a->klen] == ' ')
-     a->cbs.exec(aTHX_ rx, node, reginfo, st);
-   }
-  }
- }
-}
-
 /* --- Custom regexp engine ------------------------------------------------ */
 
 #if PERL_VERSION <= 10
@@ -165,6 +127,7 @@ EXTERN_C char *  reh_re_intuit_start(pTHX_ REGEXP * const, SV *, char *,
                                            char *, U32, re_scream_pos_data *);
 EXTERN_C SV *    reh_re_intuit_string(pTHX_ REGEXP * const);
 EXTERN_C void    reh_regfree(pTHX_ REGEXP * const);
+EXTERN_C void    reh_re_free(pTHX_ REGEXP * const);
 EXTERN_C void    reh_reg_numbered_buff_fetch(pTHX_ REGEXP * const,
                                                    const I32, SV * const);
 EXTERN_C void    reh_reg_numbered_buff_store(pTHX_ REGEXP * const,
@@ -178,34 +141,17 @@ EXTERN_C SV *    reh_reg_named_buff_iter(pTHX_ REGEXP * const,
 EXTERN_C SV *    reh_reg_qr_package(pTHX_ REGEXP * const);
 #ifdef USE_ITHREADS
 EXTERN_C void *  reh_regdupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
+EXTERN_C void *  reh_re_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
 #endif
 
 EXTERN_C const struct regexp_engine reh_regexp_engine;
 
-REGEXP *
-#if PERL_VERSION <= 10
-reh_re_compile(pTHX_ const SV * const pattern, const U32 flags)
-#else
-reh_re_compile(pTHX_ SV * const pattern, U32 flags)
-#endif
-{
- struct regexp *rx;
- REGEXP        *RX;
-
- RX = reh_regcomp(aTHX_ pattern, flags);
- rx = rxREGEXP(RX);
-
- rx->engine = &reh_regexp_engine;
-
- return RX;
-}
-
 const struct regexp_engine reh_regexp_engine = {
- reh_re_compile,
+ reh_regcomp,
  reh_regexec,
  reh_re_intuit_start,
  reh_re_intuit_string,
- reh_regfree,
+ reh_re_free,
  reh_reg_numbered_buff_fetch,
  reh_reg_numbered_buff_store,
  reh_reg_numbered_buff_length,
@@ -213,10 +159,185 @@ const struct regexp_engine reh_regexp_engine = {
  reh_reg_named_buff_iter,
  reh_reg_qr_package,
 #if defined(USE_ITHREADS)
- reh_regdupe
+ reh_re_dupe
 #endif
 };
 
+/* --- Private API --------------------------------------------------------- */
+
+typedef struct {
+ size_t            count;
+ const reh_config *cbs;
+ U32               refcount;
+} reh_private;
+
+STATIC void reh_private_free(pTHX_ reh_private *priv) {
+#define reh_private_free(P) reh_private_free(aTHX_ (P))
+ if (priv->refcount <= 1) {
+  PerlMemShared_free((void *) priv->cbs);
+  PerlMemShared_free(priv);
+ } else {
+  --priv->refcount;
+ }
+}
+
+#define PTABLE_NAME        ptable_private
+#define PTABLE_VAL_FREE(V) reh_private_free(V)
+
+#define pPTBL  pTHX
+#define pPTBL_ pTHX_
+#define aPTBL  aTHX
+#define aPTBL_ aTHX_
+
+#include "ptable.h"
+
+#define ptable_private_store(T, K, V) ptable_private_store(aTHX_ (T), (K), (V))
+#define ptable_private_delete(T, K)   ptable_private_delete(aTHX_ (T), (K))
+#define ptable_private_clear(T)       ptable_private_clear(aTHX_ (T))
+#define ptable_private_free(T)        ptable_private_free(aTHX_ (T))
+
+STATIC ptable *reh_private_map;
+
+#ifdef USE_ITHREADS
+
+STATIC perl_mutex reh_private_map_mutex;
+
+#endif /* USE_ITHREADS */
+
+#define REH_PRIVATE_MAP_FOREACH(C) STMT_START {      \
+ reh_private *priv;                                  \
+ REH_LOCK(&reh_private_map_mutex);                   \
+ priv = ptable_fetch(reh_private_map, rx->pprivate); \
+ if (priv) {                                         \
+  const reh_config *cbs = priv->cbs;                 \
+  if (cbs) {                                         \
+   const reh_config *end = cbs + priv->count;        \
+   for (; cbs < end; ++cbs) {                        \
+    (C);                                             \
+   }                                                 \
+  }                                                  \
+ }                                                   \
+ REH_UNLOCK(&reh_private_map_mutex);                 \
+} STMT_END
+
+STATIC reh_private *reh_private_map_store(pTHX_ void *ri, reh_private *priv) {
+#define reh_private_map_store(R, P) reh_private_map_store(aTHX_ (R), (P))
+ REH_LOCK(&reh_private_map_mutex);
+ ptable_private_store(reh_private_map, ri, priv);
+ REH_UNLOCK(&reh_private_map_mutex);
+
+ return;
+}
+
+STATIC void reh_private_map_copy(pTHX_ void *ri_from, void *ri_to) {
+#define reh_private_map_copy(F, T) reh_private_map_copy(aTHX_ (F), (T))
+ reh_private *priv;
+
+ REH_LOCK(&reh_private_map_mutex);
+ priv = ptable_fetch(reh_private_map, ri_from);
+ if (priv) {
+  ++priv->refcount;
+  ptable_private_store(reh_private_map, ri_to, priv);
+ }
+ REH_UNLOCK(&reh_private_map_mutex);
+}
+
+STATIC void reh_private_map_delete(pTHX_ void *ri) {
+#define reh_private_map_delete(R) reh_private_map_delete(aTHX_ (R))
+ REH_LOCK(&reh_private_map_mutex);
+ ptable_private_delete(reh_private_map, ri);
+ REH_UNLOCK(&reh_private_map_mutex);
+
+ return;
+}
+
+void reh_call_comp_begin_hook(pTHX_ regexp *rx) {
+ SV *hint = reh_hint();
+
+ if (hint && SvPOK(hint)) {
+  STRLEN      len;
+  const char *keys  = SvPV_const(hint, len);
+  size_t      count = 0;
+
+  reh_private *priv;
+  reh_config  *cbs = NULL;
+  reh_action  *a, *root;
+
+  REH_LOCK(&reh_action_list_mutex);
+  root = reh_action_list;
+  REH_UNLOCK(&reh_action_list_mutex);
+
+  for (a = root; a; a = a->next) {
+   char *p = strstr(keys, a->key);
+
+   if (p && (p + a->klen <= keys + len) && p[a->klen] == ' ')
+    ++count;
+  }
+
+  if (count) {
+   size_t i = 0;
+
+   cbs = PerlMemShared_malloc(count * sizeof *cbs);
+
+   for (a = root; a; a = a->next) {
+    char *p = strstr(keys, a->key);
+
+    if (p && (p + a->klen <= keys + len) && p[a->klen] == ' ')
+     cbs[i++] = a->cbs;
+   }
+  }
+
+  priv = PerlMemShared_malloc(sizeof *priv);
+  priv->count    = count;
+  priv->cbs      = cbs;
+  priv->refcount = 1;
+
+  rx->engine = &reh_regexp_engine;
+
+  reh_private_map_store(rx->pprivate, priv);
+ }
+}
+
+void reh_call_comp_hook(pTHX_ regexp *rx, regnode *node) {
+ REH_PRIVATE_MAP_FOREACH(cbs->comp(aTHX_ rx, node));
+}
+
+void reh_call_exec_hook(pTHX_ regexp *rx, regnode *node, regmatch_info *reginfo, regmatch_state *st) {
+ REH_PRIVATE_MAP_FOREACH(cbs->exec(aTHX_ rx, node, reginfo, st));
+}
+
+void reh_re_free(pTHX_ REGEXP * const RX) {
+ regexp *rx = rxREGEXP(RX);
+
+ reh_private_map_delete(rx->pprivate);
+
+ reh_regfree(aTHX_ RX);
+}
+
+#ifdef USE_ITHREADS
+
+void *reh_re_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param) {
+ regexp *rx = rxREGEXP(RX);
+ void   *new_ri;
+
+ new_ri = reh_regdupe(aTHX_ RX, param);
+
+ reh_private_map_copy(rx->pprivate, new_ri);
+
+ return new_ri;
+}
+
+#endif
+
+STATIC void reh_teardown(pTHX_ void *root) {
+#if REH_MULTIPLICITY
+ if (aTHX != root)
+  return;
+#endif
+
+ ptable_private_free(reh_private_map);
+}
+
 /* --- XS ------------------------------------------------------------------ */
 
 MODULE = re::engine::Hooks          PACKAGE = re::engine::Hooks
@@ -225,11 +346,17 @@ PROTOTYPES: ENABLE
 
 BOOT:
 {
+ reh_private_map = ptable_new();
 #ifdef USE_ITHREADS
  MUTEX_INIT(&reh_action_list_mutex);
+ MUTEX_INIT(&reh_private_map_mutex);
 #endif
-
  PERL_HASH(reh_hash, __PACKAGE__, __PACKAGE_LEN__);
+#if REH_MULTIPLICITY
+ call_atexit(reh_teardown, aTHX);
+#else
+ call_atexit(reh_teardown, NULL);
+#endif
 }
 
 void