Skip to content

Commit ab4caeb

Browse files
committed
Improve emulator event handling
There could be multiple subscription to the same event of the same object. These subscription need to all be triggered. The core's event handling on the Python side had a bug where only a single handler for a particular object/event combination could be registered. This commit fixes this by reworking how event subscriptions are handled. Now, each subscription will return a "token" - an opaque unique value to identify the subscription. Using tokens allows to uniquely identify each subscription, regarless for which event and object they are.
1 parent d515cd4 commit ab4caeb

6 files changed

Lines changed: 110 additions & 104 deletions

File tree

controllers/__init__.py

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -501,37 +501,34 @@ def exec_command(self, command_id, object_id, subcommand_id, opts=None):
501501
return self._virtual_objects[object_id].exec_command(command_id,
502502
subcommand_id, opts)
503503

504-
def event_register(self, object_type, event_type, object_name, handler):
505-
if not super().event_register(object_type, event_type, object_name,
506-
self._event_handler):
507-
return False
508-
self._event_handlers[(object_type, event_type, object_name)] = handler
509-
return True
504+
def event_register(self, klass, event_type, object_name, handler):
505+
subscription_id = super().event_register(klass, event_type, object_name,
506+
self._event_handler)
507+
if subscription_id is None:
508+
return None
509+
self._event_handlers[subscription_id] = handler
510+
return subscription_id
510511

511-
def event_unregister(self, object_type, event_type, object_name):
512-
self._event_handlers.pop((object_type, event_type, object_name))
513-
return super().event_unregister(object_type, event_type, object_name)
512+
def event_unregister(self, subscription_id):
513+
self._event_handlers.pop(subscription_id)
514+
return super().event_unregister(subscription_id)
514515

515516
def _find_event_data(self, klass, event):
516517
for e, s in self.object_defs[klass].events.items():
517518
if e == event:
518519
return s
519520
return None
520521

521-
def _event_handler(self, klass, object_name, event_type, data):
522-
handler = self._event_handlers.get((klass, event_type, object_name),
523-
None)
524-
if handler is None:
525-
return
526-
if self.object_defs[klass].virtual:
527-
handler(klass, event_type, object_name, data)
528-
return
529-
event_data_def = self._find_event_data(klass, event_type)
530-
if event_data_def is None:
531-
raise vortex.core.VortexCoreError(f"Unknown event type {event_type}")
532-
pointer = ctypes.cast(data, ctypes.POINTER(event_data_def))
533-
content = vortex.lib.ctypes_helpers.parse_ctypes_struct(pointer.contents)
534-
handler(klass, event_type, object_name, content)
522+
def _event_handler(self, token, klass, object_name, event_type, data):
523+
if not self.object_defs[klass].virtual:
524+
event_data_def = self._find_event_data(klass, event_type)
525+
if event_data_def is None:
526+
raise vortex.core.VortexCoreError(f"Unknown event type {event_type}")
527+
pointer = ctypes.cast(data, ctypes.POINTER(event_data_def))
528+
data = vortex.lib.ctypes_helpers.parse_ctypes_struct(pointer.contents)
529+
530+
handler = self._event_handlers.get(token)
531+
handler(klass, event_type, object_name, data)
535532

536533
def reset(self, objects=[]):
537534
if not objects:

frontends/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ def __init__(self, queue_size=0):
4343
self._queue = CommandQueue(queue_size)
4444
self._thread = None
4545
self.is_reset = False
46-
self.event_register = lambda a, b, c, d: False
47-
self.event_unregister = lambda a, b, c, d: False
4846
self.emulation_frequency = 0
4947
try:
5048
os.mkfifo(self.PIPE_PATH)
@@ -62,7 +60,7 @@ def set_controller(self, controller):
6260
self._set_controller_data()
6361

6462
# Implement these as class methods so subclasses can
65-
# override them. If they they are implemented as class
63+
# override them. If they are implemented as class
6664
# attributes, any subclass lookup of those methods will
6765
# return the base class method instead of the overriden
6866
# one.

src/core/common_defs.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ typedef struct {
4343
object_list_cb_t object_list;
4444
complete_cb_t completion_callback;
4545
event_register_t event_register;
46-
event_register_t event_unregister;
46+
event_unregister_t event_unregister;
4747
event_submit_t event_submit;
4848
cmd_submit_cb_t cmd_submit;
4949
void *v_cmd_exec;
@@ -171,11 +171,9 @@ static inline core_object_t *core_id_to_object(core_object_id_t id) {
171171
->call_data.event_register( \
172172
(type), (event), (name), ((core_object_t *)(obj)), (handler), \
173173
((core_object_t *)(obj))->call_data.cb_data))
174-
#define CORE_EVENT_UNREGISTER(obj, type, event, name) \
175-
(((core_object_t *)(obj)) \
176-
->call_data.event_unregister( \
177-
(type), (event), (name), ((core_object_t *)(obj)), (handler), \
178-
((core_object_t *)(obj))->call_data.cb_data))
174+
#define CORE_EVENT_UNREGISTER(obj, token) \
175+
(((core_object_t *)(obj)) \
176+
->call_data.event_unregister((token), ((core_object_t *)(obj))->call_data.cb_data))
179177
#define CORE_EVENT_SUBMIT(obj, event, data) \
180178
(((core_object_t *)(obj)) \
181179
->call_data.event_submit( \

src/core/core.c

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,14 @@ static core_object_t **core_object_list(const core_object_klass_t klass,
160160
/* Object callbacks */
161161
static void core_object_command_complete(uint64_t command_id, int64_t result,
162162
void *, void *data);
163-
static int core_object_event_register(const core_object_klass_t klass,
164-
const core_object_event_type_t event,
165-
const char *name, core_object_t *object,
166-
event_handler_t handler, void *user_data);
167-
static int core_object_event_unregister(const core_object_klass_t klass,
168-
const core_object_event_type_t event,
169-
const char *name, core_object_t *object,
170-
event_handler_t handler,
171-
void *user_data);
163+
static core_event_token_t core_object_event_register(const core_object_klass_t klass,
164+
const core_object_event_type_t event,
165+
const char *name,
166+
core_object_t *object,
167+
event_handler_t handler,
168+
void *user_data);
169+
170+
static int core_object_event_unregister(const core_event_token_t token, void *user_data);
172171
static int core_object_event_submit(const core_object_event_type_t event,
173172
const core_object_id_t id, void *event_data,
174173
void *user_data);
@@ -265,8 +264,8 @@ static void core_process_events(void *arg) {
265264
event->data);
266265
else {
267266
PyGILState_STATE state = PyGILState_Ensure();
268-
PyObject *args = Py_BuildValue("(isik)", object->klass,
269-
object->name, event->type,
267+
PyObject *args = Py_BuildValue("(kisik)", (core_event_token_t)subscription,
268+
object->klass, object->name, event->type,
270269
event->data);
271270
if (!args) {
272271
PyErr_Print();
@@ -1149,10 +1148,12 @@ static PyObject *vortex_core_get_status(PyObject *self, PyObject *args) {
11491148
return NULL;
11501149
}
11511150

1152-
static int core_object_event_register(const core_object_klass_t klass,
1153-
const core_object_event_type_t event,
1154-
const char *name, core_object_t *object,
1155-
event_handler_t handler, void *data) {
1151+
static unsigned long core_object_event_register(const core_object_klass_t klass,
1152+
const core_object_event_type_t event,
1153+
const char *name,
1154+
core_object_t *object,
1155+
event_handler_t handler,
1156+
void *data) {
11561157
core_t *core = (core_t *)data;
11571158
event_subscription_t *subscription;
11581159

@@ -1180,35 +1181,32 @@ static int core_object_event_register(const core_object_klass_t klass,
11801181
pthread_mutex_lock(&core->events.handlers[event].lock);
11811182
STAILQ_INSERT_TAIL(&core->events.handlers[event].list, subscription, entry);
11821183
pthread_mutex_unlock(&core->events.handlers[event].lock);
1183-
return 0;
1184+
return (unsigned long)subscription;
11841185
}
11851186

1186-
static int core_object_event_unregister(const core_object_klass_t klass,
1187-
const core_object_event_type_t event,
1188-
const char *name, core_object_t *object,
1189-
event_handler_t handler, void *data) {
1187+
static int core_object_event_unregister(const core_event_token_t token, void *data) {
11901188
core_t *core = (core_t *)data;
11911189
event_subscription_t *subscription;
11921190
event_subscription_t *next;
1193-
core_object_t *obj = core_object_find(klass, name, core);
1191+
core_object_event_type_t event;
11941192

1195-
pthread_mutex_lock(&core->events.handlers[event].lock);
1196-
subscription = STAILQ_FIRST(&core->events.handlers[event].list);
1197-
pthread_mutex_unlock(&core->events.handlers[event].lock);
1198-
while (subscription != NULL) {
1199-
next = STAILQ_NEXT(subscription, entry);
1200-
if (subscription->klass == klass &&
1201-
(subscription->object_id == CORE_OBJECT_ID_INVALID ||
1202-
(object && subscription->object_id == core_object_to_id(obj)))) {
1203-
pthread_mutex_lock(&core->events.handlers[event].lock);
1204-
STAILQ_REMOVE(&core->events.handlers[event].list, subscription,
1205-
event_subscription, entry);
1206-
pthread_mutex_unlock(&core->events.handlers[event].lock);
1207-
free(subscription);
1208-
return 0;
1209-
}
1193+
for (event = OBJECT_EVENT_STEPPER_MOVE_COMPLETE; event <= OBJECT_EVENT_MAX; event++) {
1194+
pthread_mutex_lock(&core->events.handlers[event].lock);
1195+
subscription = STAILQ_FIRST(&core->events.handlers[event].list);
1196+
pthread_mutex_unlock(&core->events.handlers[event].lock);
1197+
while (subscription != NULL) {
1198+
next = STAILQ_NEXT(subscription, entry);
1199+
if ((core_event_token_t)subscription == token) {
1200+
pthread_mutex_lock(&core->events.handlers[event].lock);
1201+
STAILQ_REMOVE(&core->events.handlers[event].list, subscription, event_subscription,
1202+
entry);
1203+
pthread_mutex_unlock(&core->events.handlers[event].lock);
1204+
free(subscription);
1205+
return 0;
1206+
}
12101207

1211-
subscription = next;
1208+
subscription = next;
1209+
}
12121210
}
12131211

12141212
return -1;
@@ -1272,13 +1270,19 @@ static uint64_t core_object_command_submit(core_object_t *source,
12721270
return (uint64_t)cmd;
12731271
}
12741272

1273+
#define EVENT_TOKEN(klass, type, name, sub) \
1274+
(core_event_token_t)(((unsigned long)type << 60) | ((unsigned long)sub & ((1UL << 60) - 1)))
1275+
1276+
#define TYPE_FROM_TOKEN(token) (core_object_event_type_t)((token >> 60) & 0xf)
1277+
12751278
static PyObject *vortex_core_python_event_register(PyObject *self,
12761279
PyObject *args) {
12771280
core_t *core = (core_t *)self;
12781281
event_subscription_t *subscription;
12791282
core_object_klass_t klass;
12801283
core_object_event_type_t type;
12811284
PyObject *callback;
1285+
PyObject *token;
12821286
char *name = NULL;
12831287

12841288
if (!PyArg_ParseTuple(args, "iisO", &klass, &type, &name, &callback))
@@ -1287,15 +1291,18 @@ static PyObject *vortex_core_python_event_register(PyObject *self,
12871291
Py_INCREF(callback);
12881292

12891293
subscription = calloc(1, sizeof(*subscription));
1290-
if (!subscription)
1291-
Py_RETURN_FALSE;
1294+
if (!subscription) {
1295+
Py_DECREF(callback);
1296+
Py_RETURN_NONE;
1297+
}
12921298

12931299
if (name) {
12941300
core_object_t *object = core_object_find(klass, name, core);
12951301

12961302
if (!object) {
12971303
free(subscription);
1298-
Py_RETURN_FALSE;
1304+
Py_DECREF(callback);
1305+
Py_RETURN_NONE;
12991306
}
13001307

13011308
subscription->object_id = core_object_to_id(object);
@@ -1309,49 +1316,40 @@ static PyObject *vortex_core_python_event_register(PyObject *self,
13091316
pthread_mutex_lock(&core->events.handlers[type].lock);
13101317
STAILQ_INSERT_TAIL(&core->events.handlers[type].list, subscription, entry);
13111318
pthread_mutex_unlock(&core->events.handlers[type].lock);
1312-
Py_RETURN_TRUE;
1319+
token = Py_BuildValue("k", EVENT_TOKEN(klass, type, name, subscription));
1320+
return token;
13131321
}
13141322

13151323
static PyObject *vortex_core_python_event_unregister(PyObject *self,
13161324
PyObject *args) {
13171325
core_t *core = (core_t *)self;
1318-
event_subscription_t *subscription;
1319-
event_subscription_t *next;
1320-
core_object_klass_t klass;
1326+
core_event_token_t subscription;
13211327
core_object_event_type_t type;
1322-
core_object_id_t object_id = CORE_OBJECT_ID_INVALID;
1323-
char *name;
1328+
event_subscription_t *iter;
1329+
event_subscription_t *next;
13241330

1325-
if (PyArg_ParseTuple(args, "iis", &klass, &type, &name))
1331+
if (!PyArg_ParseTuple(args, "k", &subscription))
13261332
return NULL;
13271333

1328-
if (name) {
1329-
core_object_t *object = core_object_find(klass, name, core);
1330-
1331-
if (object)
1332-
object_id = core_object_to_id(object);
1333-
}
1334+
type = TYPE_FROM_TOKEN(subscription);
13341335

13351336
pthread_mutex_lock(&core->events.handlers[type].lock);
1336-
subscription = STAILQ_FIRST(&core->events.handlers[type].list);
1337+
iter = STAILQ_FIRST(&core->events.handlers[type].list);
13371338
pthread_mutex_unlock(&core->events.handlers[type].lock);
13381339

1339-
while (subscription != NULL) {
1340-
next = STAILQ_NEXT(subscription, entry);
1341-
if (subscription->klass == klass &&
1342-
(subscription->object_id == CORE_OBJECT_ID_INVALID ||
1343-
subscription->object_id == object_id)) {
1340+
while (iter != NULL) {
1341+
next = STAILQ_NEXT(iter, entry);
1342+
if (subscription == (core_event_token_t)iter) {
13441343
pthread_mutex_lock(&core->events.handlers[type].lock);
1345-
STAILQ_REMOVE(&core->events.handlers[type].list, subscription,
1346-
event_subscription, entry);
1344+
STAILQ_REMOVE(&core->events.handlers[type].list, iter, event_subscription, entry);
13471345
pthread_mutex_unlock(&core->events.handlers[type].lock);
1348-
if (subscription->is_python)
1349-
Py_DECREF(subscription->python.handler);
1350-
free(subscription);
1346+
if (iter->is_python)
1347+
Py_DECREF(iter->python.handler);
1348+
free(iter);
13511349
Py_RETURN_TRUE;
13521350
}
13531351

1354-
subscription = next;
1352+
iter = next;
13551353
}
13561354

13571355
Py_RETURN_FALSE;

src/core/events.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ typedef struct {
4949
double position[AXIS_TYPE_MAX];
5050
} toolhead_origin_event_data_t;
5151

52+
typedef unsigned long core_event_token_t;
53+
5254
/*
5355
* Object event handler type.
5456
* Object event handlers are called to handle events from
@@ -63,17 +65,28 @@ typedef void (*event_handler_t)(core_object_t *, const char *,
6365
const core_object_event_type_t, void *);
6466

6567
/*
66-
* Signature of call to (un)register for an object event.
68+
* Signature of call to register for an object event.
6769
* The arguments are:
6870
* - the event type to register for,
6971
* - name of the object issuing the event,
7072
* - object registering for the event,
7173
* - the object's event handler function,
7274
* - the data that needs to be passed.
7375
*/
74-
typedef int (*event_register_t)(const core_object_klass_t,
75-
const core_object_event_type_t, const char *,
76-
core_object_t *, event_handler_t, void *);
76+
typedef core_event_token_t (*event_register_t)(const core_object_klass_t,
77+
const core_object_event_type_t,
78+
const char *,
79+
core_object_t *,
80+
event_handler_t,
81+
void *);
82+
83+
/*
84+
* Signature of call to unregister for an object event.
85+
* The arguments are:
86+
* = the event subscription token.
87+
* - the data that needs to be passed.
88+
*/
89+
typedef int (*event_unregister_t)(const core_event_token_t, void *);
7790

7891
/*
7992
* Signature of call to issue an event.

src/core/objects/axis.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct {
6767
float length;
6868
double start_position;
6969
double position;
70+
core_event_token_t endstop_event_token;
7071
stepper_status_t stepper_status;
7172
} axis_t;
7273

@@ -197,9 +198,9 @@ static int axis_init(core_object_t *object) {
197198
axis->endstop_is_max = false;
198199
}
199200

200-
CORE_EVENT_REGISTER(axis, OBJECT_KLASS_ENDSTOP,
201-
OBJECT_EVENT_ENDSTOP_TRIGGER, axis->endstop_name,
202-
axis_event_handler);
201+
axis->endstop_event_token = CORE_EVENT_REGISTER(axis, OBJECT_KLASS_ENDSTOP,
202+
OBJECT_EVENT_ENDSTOP_TRIGGER,
203+
axis->endstop_name, axis_event_handler);
203204
axis_reset(object);
204205
return 0;
205206
}
@@ -347,6 +348,7 @@ static void axis_destroy(core_object_t *object) {
347348
axis_t *axis = (axis_t *)object;
348349
size_t i;
349350

351+
CORE_EVENT_UNREGISTER(axis, axis->endstop_event_token);
350352
core_object_destroy(object);
351353
object_cache_destroy(axis_event_cache);
352354
free((char *)axis->endstop_name);

0 commit comments

Comments
 (0)