#include "cReactor.h"
#include <stdio.h>
#include <unistd.h>
struct _cEventTriggers
{
char * event_type;
cEventTriggers * next;
cReactorMethod * triggers[CREACTOR_NUM_EVENT_PHASES];
int before_finished;
PyObject * defer_list;
};
static cEventTriggers *get_event_triggers(cReactor *reactor,
const char *event_type,
int create);
static PyObject *system_event_defer_callback(PyObject *self,
PyObject *args);
typedef struct _SysEventInfo SysEventInfo;
struct _SysEventInfo
{
cReactor * reactor;
const char * event_type;
int got_defers;
};
static PyMethodDef callback_def = {
"system_event_defer_callback",
system_event_defer_callback,
METH_VARARGS,
"system_event_defer_callback",
};
static void
run_system_event_triggers(PyObject *callable, PyObject *args, PyObject *kw, void *user_data)
{
PyObject *result;
UNUSED(user_data);
result = PyEval_CallObjectWithKeywords(callable, args, kw);
if (!result)
{
PyErr_Print();
return;
}
Py_DECREF(result);
}
static void
finish_system_event(cReactor *reactor, cEventTriggers *triggers)
{
cReactorUtil_ForEachMethod(triggers->triggers[CREACTOR_EVENT_PHASE_DURING],
run_system_event_triggers, NULL);
cReactorUtil_ForEachMethod(triggers->triggers[CREACTOR_EVENT_PHASE_AFTER],
run_system_event_triggers, NULL);
if (strcmp(triggers->event_type, "shutdown") == 0)
cReactor_stop_finish(reactor);
}
static PyObject *
system_event_defer_callback(PyObject *self, PyObject *args)
{
PyObject *defer_id;
void *defer;
int i;
int len;
PyObject *result;
PyObject *empty_list;
cReactor *reactor;
const char *event_type;
cEventTriggers *triggers;
reactor = (cReactor *)self;
if (!PyArg_ParseTuple(args, "OOs:system_event_defer_callback",
&result, &defer_id, &event_type))
{
return NULL;
}
defer = PyLong_AsVoidPtr(defer_id);
if (PyErr_Occurred()) {
return NULL;
}
triggers = get_event_triggers(reactor, event_type, 0);
if (!triggers) {
PyErr_Format(PyExc_ValueError,
"system_event_defer_callback arg 2 refers "
"to non-existent event type: %s", event_type);
return NULL;
}
len = PyList_Size(triggers->defer_list);
for (i = 0; i < len; ++i)
{
if (PyList_GetItem(triggers->defer_list, i) == defer)
{
empty_list = PyList_New(0);
PyList_SetSlice(triggers->defer_list, i, i + 1, empty_list);
Py_DECREF(empty_list);
break;
}
}
if (triggers->before_finished &&
PyList_Size(triggers->defer_list) == 0)
{
finish_system_event(reactor, triggers);
}
Py_INCREF(Py_None);
return Py_None;
}
static void
run_before_system_event_triggers(PyObject *callable, PyObject *args, PyObject *kw, void *user_data)
{
PyObject *defer, *defer_id;
PyObject *deferred_class;
PyObject *result;
int is_defer;
PyObject *callback;
cReactor *reactor;
SysEventInfo *event_info;
cEventTriggers *triggers;
event_info = (SysEventInfo *)user_data;
reactor = event_info->reactor;
deferred_class = cReactorUtil_FromImport("twisted.internet.defer",
"Deferred");
if (!deferred_class)
{
PyErr_Print();
return;
}
defer = PyEval_CallObjectWithKeywords(callable, args, kw);
if (!defer)
{
Py_DECREF(deferred_class);
return;
}
is_defer = PyObject_IsInstance(defer, deferred_class);
Py_DECREF(deferred_class);
if (!is_defer)
{
Py_DECREF(defer);
return;
}
triggers = get_event_triggers(reactor, event_info->event_type, 0);
if (!triggers) {
PyErr_Format(PyExc_RuntimeError,
"They're Gone! "
"The cEventTriggers structure for '%s' vanished!",
event_info->event_type);
PyErr_Print();
Py_DECREF(defer);
return;
}
event_info->got_defers = 1;
if (PyList_Append(triggers->defer_list, defer) < 0)
{
Py_DECREF(defer);
PyErr_Print();
return;
}
callback = PyCFunction_New(&callback_def, (PyObject *)reactor);
defer_id = PyLong_FromVoidPtr(defer);
result = PyObject_CallMethod(defer, "addBoth", "(OOs)",
callback, defer_id, triggers->event_type);
Py_DECREF(callback);
Py_DECREF(defer);
Py_DECREF(defer_id);
Py_XDECREF(result);
if (!result)
{
PyErr_Print();
}
}
void
fireSystemEvent_internal(cReactor *reactor, const char *event_type)
{
SysEventInfo event_info;
cEventTriggers *triggers;
triggers = get_event_triggers(reactor, event_type, 0);
if (!triggers) {
if (strcmp(event_type, "shutdown") == 0)
cReactor_stop_finish(reactor);
return;
}
triggers->before_finished = 0;
event_info.reactor = reactor;
event_info.event_type = event_type;
event_info.got_defers = 0;
cReactorUtil_ForEachMethod(triggers->triggers[CREACTOR_EVENT_PHASE_BEFORE],
run_before_system_event_triggers, &event_info);
triggers->before_finished = 1;
if (! event_info.got_defers ||
PyList_Size(triggers->defer_list) == 0)
{
finish_system_event(reactor, triggers);
}
}
PyObject *
cReactor_fireSystemEvent(PyObject *self, PyObject *args)
{
cReactor *reactor;
const char *type_str;
reactor = (cReactor *)self;
if (!PyArg_ParseTuple(args, "s:fireSystemEvent", &type_str))
{
return NULL;
}
fireSystemEvent_internal(reactor, type_str);
Py_INCREF(Py_None);
return Py_None;
}
static cEventTriggers *
get_event_triggers(cReactor *reactor, const char *event_type, int create)
{
cEventTriggers *node;
node = reactor->event_triggers;
while (node) {
if (strcmp(node->event_type, event_type) == 0)
break;
node = node->next;
}
if (node)
return node;
if (!create)
return NULL;
node = (cEventTriggers *)malloc(sizeof(*node));
if (!node)
return NULL;
memset(node, 0, sizeof(*node));
node->event_type = strdup(event_type);
if (!node->event_type) {
free(node);
return NULL;
}
node->defer_list = PyList_New(0);
if (!node->defer_list) {
free(node->event_type);
free(node);
return NULL;
}
node->next = reactor->event_triggers;
reactor->event_triggers = node;
return node;
}
void
free_event_trigger(cEventTriggers *trigger)
{
int p;
free(trigger->event_type);
for (p = 0; p < CREACTOR_NUM_EVENT_PHASES; p++)
cReactorUtil_DestroyMethods(trigger->triggers[p]);
Py_XDECREF(trigger->defer_list);
trigger->defer_list = NULL;
free(trigger);
}
void
cSystemEvent_FreeTriggers(cEventTriggers *triggers)
{
cEventTriggers *node, *next;
node = triggers;
while (node) {
next = node->next;
free_event_trigger(node);
node = next;
}
}
int
cReactorUtil_GetEventPhase(const char *str, cReactorEventPhase *out_phase)
{
static struct {
const char * str;
cReactorEventPhase phase;
} phase_map[] =
{
{ "before", CREACTOR_EVENT_PHASE_BEFORE },
{ "during", CREACTOR_EVENT_PHASE_DURING },
{ "after", CREACTOR_EVENT_PHASE_AFTER },
};
static int phase_map_len = sizeof(phase_map) / sizeof(phase_map[0]);
int i;
for (i = 0; i < phase_map_len; ++i)
{
if (strcmp(str, phase_map[i].str) == 0)
{
*out_phase = phase_map[i].phase;
return 0;
}
}
PyErr_Format(PyExc_ValueError, "unknown event phase: %s", str);
return -1;
}
PyObject *
cReactor_addSystemEventTrigger(PyObject *self, PyObject *args, PyObject *kw)
{
cReactor *reactor;
int method_id;
PyObject *req_args = NULL;
PyObject *callable_args = NULL;
PyObject *callable = NULL;
const char *phase_str = NULL;
const char *event_type_str = NULL;
cEventTriggers *triggers;
cReactorEventPhase event_phase;
reactor = (cReactor *)self;
req_args = PyTuple_GetSlice(args, 0, 3);
if (!PyArg_ParseTuple(req_args, "ssO:addSystemEventTrigger",
&phase_str, &event_type_str, &callable))
{
Py_DECREF(req_args);
return NULL;
}
Py_DECREF(req_args);
if (cReactorUtil_GetEventPhase(phase_str, &event_phase) < 0)
{
return NULL;
}
if (!PyCallable_Check(callable))
{
PyErr_Format(PyExc_TypeError,
"addSystemEventTrigger() arg 3 expected callable, got %s",
callable->ob_type->tp_name);
return NULL;
}
triggers = get_event_triggers(reactor, event_type_str, 1);
if (!triggers) {
PyErr_SetString(PyExc_MemoryError,
"could not allocate cEventTriggers struct");
return NULL;
}
callable_args = PyTuple_GetSlice(args, 3, PyTuple_Size(args));
method_id = cReactorUtil_AddMethod(&triggers->triggers[event_phase],
callable, callable_args, kw);
Py_DECREF(callable_args);
return PyInt_FromLong(method_id);
}
PyObject *
cReactor_removeSystemEventTrigger(PyObject *self, PyObject *args)
{
cReactor *reactor = (cReactor *)self;
int method_id;
cEventTriggers *node, **last;
int have_triggers, found = 0;
int p;
if (!PyArg_ParseTuple(args, "i:removeSystemEventTrigger", &method_id))
{
return NULL;
}
node = reactor->event_triggers;
while (node) {
for (p = 0; p < CREACTOR_NUM_EVENT_PHASES; p++)
if (cReactorUtil_RemoveMethod(&node->triggers[p], method_id) == 0)
found++;
node = node->next;
}
last = &reactor->event_triggers;
while(*last) {
node = *last;
have_triggers = 0;
for (p = 0; p < CREACTOR_NUM_EVENT_PHASES; p++)
if (node->triggers[p])
have_triggers++;
if (have_triggers) {
last = &(node->next);
} else {
*last = node->next;
free_event_trigger(node);
}
}
if (!found) {
PyErr_Format(PyExc_ValueError, "invalid method_id %d", method_id);
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}