# error "Perl does not support ithreads!"
#endif /* !defined(USE_ITHREADS) */
+/* clear the Perl sub's stack frame
+ * (this should only be used inside an XSUB) */
+#define CLEAR_STACK_FRAME PL_stack_sp = PL_stack_base + *PL_markstack_ptr
+
#define PLUGIN_INIT 0
#define PLUGIN_READ 1
#define PLUGIN_WRITE 2
static XS (Collectd_plugin_unregister_ds);
static XS (Collectd_plugin_dispatch_values);
static XS (Collectd_plugin_log);
+static XS (Collectd_call_by_name);
/*
* private data types
c_ithread_t *head;
c_ithread_t *tail;
+#if COLLECT_DEBUG
+ /* some usage stats */
+ int number_of_threads;
+#endif /* COLLECT_DEBUG */
+
pthread_mutex_t mutex;
} c_ithread_list_t;
* point to the "base" thread */
static c_ithread_list_t *perl_threads = NULL;
+/* the key used to store each pthread's ithread */
+static pthread_key_t perl_thr_key;
+
static int perl_argc = 0;
static char **perl_argv = NULL;
{ "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
{ "Collectd::plugin_dispatch_values", Collectd_plugin_dispatch_values },
{ "Collectd::plugin_log", Collectd_plugin_log },
+ { "Collectd::call_by_name", Collectd_call_by_name },
{ "", NULL }
};
} /* static XS (Collectd_plugin_log) */
/*
+ * Collectd::call_by_name (...).
+ *
+ * Call a Perl sub identified by its name passed through $Collectd::cb_name.
+ */
+static XS (Collectd_call_by_name)
+{
+ SV *tmp = NULL;
+ char *name = NULL;
+
+ if (NULL == (tmp = get_sv ("Collectd::cb_name", 0))) {
+ sv_setpv (get_sv ("@", 1), "cb_name has not been set");
+ CLEAR_STACK_FRAME;
+ return;
+ }
+
+ name = SvPV_nolen (tmp);
+
+ if (NULL == get_cv (name, 0)) {
+ sv_setpvf (get_sv ("@", 1), "unknown callback \"%s\"", name);
+ CLEAR_STACK_FRAME;
+ return;
+ }
+
+ /* simply pass on the subroutine call without touching the stack,
+ * thus leaving any arguments and return values in place */
+ call_pv (name, 0);
+} /* static XS (Collectd_call_by_name) */
+
+/*
* collectd's perl interpreter based thread implementation.
*
* This has been inspired by Perl's ithreads introduced in version 5.6.0.
*/
/* must be called with perl_threads->mutex locked */
+static void c_ithread_destroy (c_ithread_t *ithread)
+{
+ dTHXa (ithread->interp);
+
+ assert (NULL != perl_threads);
+
+ PERL_SET_CONTEXT (aTHX);
+ log_debug ("Shutting down Perl interpreter %p...", aTHX);
+
+#if COLLECT_DEBUG
+ sv_report_used ();
+
+ --perl_threads->number_of_threads;
+#endif /* COLLECT_DEBUG */
+
+ perl_destruct (aTHX);
+ perl_free (aTHX);
+
+ if (NULL == ithread->prev)
+ perl_threads->head = ithread->next;
+ else
+ ithread->prev->next = ithread->next;
+
+ if (NULL == ithread->next)
+ perl_threads->tail = ithread->prev;
+ else
+ ithread->next->prev = ithread->prev;
+
+ sfree (ithread);
+ return;
+} /* static void c_ithread_destroy (c_ithread_t *) */
+
+static void c_ithread_destructor (void *arg)
+{
+ c_ithread_t *ithread = (c_ithread_t *)arg;
+ c_ithread_t *t = NULL;
+
+ if (NULL == perl_threads)
+ return;
+
+ pthread_mutex_lock (&perl_threads->mutex);
+
+ for (t = perl_threads->head; NULL != t; t = t->next)
+ if (t == ithread)
+ break;
+
+ /* the ithread no longer exists */
+ if (NULL == t)
+ return;
+
+ c_ithread_destroy (ithread);
+
+ pthread_mutex_unlock (&perl_threads->mutex);
+ return;
+} /* static void c_ithread_destructor (void *) */
+
+/* must be called with perl_threads->mutex locked */
static c_ithread_t *c_ithread_create (PerlInterpreter *base)
{
c_ithread_t *t = NULL;
+ dTHXa (NULL);
assert (NULL != perl_threads);
? NULL
: perl_clone (base, CLONEf_KEEP_PTR_TABLE);
+ aTHX = t->interp;
+
+ if (NULL != base) {
+ av_clear (PL_endav);
+ av_undef (PL_endav);
+ PL_endav = Nullav;
+ }
+
+#if COLLECT_DEBUG
+ ++perl_threads->number_of_threads;
+#endif /* COLLECT_DEBUG */
+
t->next = NULL;
if (NULL == perl_threads->tail) {
}
perl_threads->tail = t;
+
+ pthread_setspecific (perl_thr_key, (const void *)t);
return t;
} /* static c_ithread_t *c_ithread_create (PerlInterpreter *) */
aTHX = t->interp;
}
+
+ log_debug ("perl_init: c_ithread: interp = %p (active threads: %i)",
+ aTHX, perl_threads->number_of_threads);
return pplugin_call_all (aTHX_ PLUGIN_INIT);
} /* static int perl_init (void) */
aTHX = t->interp;
}
+
+ log_debug ("perl_read: c_ithread: interp = %p (active threads: %i)",
+ aTHX, perl_threads->number_of_threads);
return pplugin_call_all (aTHX_ PLUGIN_READ);
} /* static int perl_read (void) */
aTHX = t->interp;
}
+
+ log_debug ("perl_write: c_ithread: interp = %p (active threads: %i)",
+ aTHX, perl_threads->number_of_threads);
return pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
} /* static int perl_write (const data_set_t *, const value_list_t *) */
aTHX = t->interp;
}
+ log_debug ("perl_shutdown: c_ithread: interp = %p (active threads: %i)",
+ aTHX, perl_threads->number_of_threads);
+
plugin_unregister_log ("perl");
plugin_unregister_init ("perl");
plugin_unregister_read ("perl");
t = perl_threads->tail;
while (NULL != t) {
- aTHX = t->interp;
- PERL_SET_CONTEXT (aTHX);
-
-#if COLLECT_DEBUG
- sv_report_used ();
-#endif /* COLLECT_DEBUG */
-
- perl_destruct (aTHX);
- perl_free (aTHX);
+ c_ithread_t *thr = t;
+ /* the pointer has to be advanced before destroying
+ * the thread as this will free the memory */
t = t->prev;
- sfree (t);
+ c_ithread_destroy (thr);
}
pthread_mutex_unlock (&perl_threads->mutex);
+ pthread_mutex_destroy (&perl_threads->mutex);
sfree (perl_threads);
+ pthread_key_delete (perl_thr_key);
+
PERL_SYS_TERM ();
plugin_unregister_shutdown ("perl");
}
#endif /* COLLECT_DEBUG */
+ if (0 != pthread_key_create (&perl_thr_key, c_ithread_destructor)) {
+ log_err ("init_pi: pthread_key_create failed");
+
+ /* this must not happen - cowardly giving up if it does */
+ exit (1);
+ }
+
PERL_SYS_INIT3 (&argc, &argv, &environ);
perl_threads = (c_ithread_list_t *)smalloc (sizeof (c_ithread_list_t));