Merge pull request #380 from lancaster-university/messagebus-patch

Messagebus patch
This commit is contained in:
Joe Finney 2018-09-07 18:35:20 +01:00 committed by GitHub
commit da620c0f3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 68 deletions

View file

@ -53,10 +53,16 @@ DEALINGS IN THE SOFTWARE.
*/
class EventModel
{
protected:
void (*listener_deletion_callback)(MicroBitListener *); // if not null, this function is invoked when a listener is removed.
public:
static EventModel *defaultEventBus;
// Set listener_deletion_callback to NULL.
EventModel() : listener_deletion_callback(NULL) {}
/**
* Queues the given event to be sent to all registered recipients.
* The method of delivery will vary depending on the underlying implementation.
@ -130,6 +136,17 @@ class EventModel
return MICROBIT_OK;
}
/**
* Sets a pointer to handler that's invoked when any listener is deleted.
*
* @returns MICROBIT_OK on success.
**/
int setListenerDeletionCallback(void (*listener_deletion_callback)(MicroBitListener *))
{
this->listener_deletion_callback = listener_deletion_callback;
return MICROBIT_OK;
}
/**
* Register a listener function.
*
@ -298,6 +315,8 @@ class EventModel
* @param id The Event ID used to register the listener.
* @param value The Event value used to register the listener.
* @param handler The function used to register the listener.
* @param arg the arg that is passed to the handler on an event. Used to differentiate between handlers with the same id and source, but not the same arg.
* Defaults to NULL, which means any handler with the same id, event and callback is removed.
*
* @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler
* given is NULL.
@ -315,12 +334,12 @@ class EventModel
* uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
* @endcode
*/
int ignore(int id, int value, void (*handler)(MicroBitEvent, void*))
int ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg = NULL)
{
if (handler == NULL)
return MICROBIT_INVALID_PARAMETER;
MicroBitListener listener(id, value, handler, NULL);
MicroBitListener listener(id, value, handler, arg);
remove(&listener);
return MICROBIT_OK;

View file

@ -60,15 +60,15 @@ DEALINGS IN THE SOFTWARE.
*/
MicroBitMessageBus::MicroBitMessageBus()
{
this->listeners = NULL;
this->listeners = NULL;
this->evt_queue_head = NULL;
this->evt_queue_tail = NULL;
this->queueLength = 0;
fiber_add_idle_component(this);
fiber_add_idle_component(this);
if(EventModel::defaultEventBus == NULL)
EventModel::defaultEventBus = this;
if(EventModel::defaultEventBus == NULL)
EventModel::defaultEventBus = this;
}
/**
@ -79,7 +79,7 @@ MicroBitMessageBus::MicroBitMessageBus()
*/
void async_callback(void *param)
{
MicroBitListener *listener = (MicroBitListener *)param;
MicroBitListener *listener = (MicroBitListener *)param;
// OK, now we need to decide how to behave depending on our configuration.
// If this a fiber f already active within this listener then check our
@ -228,11 +228,11 @@ MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
*/
int MicroBitMessageBus::deleteMarkedListeners()
{
MicroBitListener *l, *p;
MicroBitListener *l, *p;
int removed = 0;
l = listeners;
p = NULL;
l = listeners;
p = NULL;
// Walk this list of event handlers. Delete any that match the given listener.
while (l != NULL)
@ -339,14 +339,14 @@ int MicroBitMessageBus::send(MicroBitEvent evt)
*/
int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
{
MicroBitListener *l;
MicroBitListener *l;
int complete = 1;
bool listenerUrgent;
l = listeners;
while (l != NULL)
{
if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
{
// If we're running under the fiber scheduler, then derive the THREADING_MODE for the callback based on the
// metadata in the listener itself.
@ -365,19 +365,19 @@ int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
// Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
// should the event handler attempt a blocking operation, but doesn't have the overhead
// of creating a fiber needlessly. (cool huh?)
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running())
async_callback(l);
else
invoke(async_callback, l);
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running())
async_callback(l);
else
invoke(async_callback, l);
}
else
{
complete = 0;
}
}
}
l = l->next;
}
l = l->next;
}
return complete;
}
@ -391,17 +391,17 @@ int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
*/
int MicroBitMessageBus::add(MicroBitListener *newListener)
{
MicroBitListener *l, *p;
MicroBitListener *l, *p;
int methodCallback;
//handler can't be NULL!
if (newListener == NULL)
return MICROBIT_INVALID_PARAMETER;
//handler can't be NULL!
if (newListener == NULL)
return MICROBIT_INVALID_PARAMETER;
l = listeners;
l = listeners;
// Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
// registered in a that will already capture these events. If we do, silently ignore.
// Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
// registered in a that will already capture these events. If we do, silently ignore.
// We always check the ID, VALUE and CB_METHOD fields.
// If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
@ -409,7 +409,7 @@ int MicroBitMessageBus::add(MicroBitListener *newListener)
{
methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb) && newListener->cb_arg == l->cb_arg)
{
// We have a perfect match for this event listener already registered.
// If it's marked for deletion, we simply resurrect the listener, and we're done.
@ -424,50 +424,50 @@ int MicroBitMessageBus::add(MicroBitListener *newListener)
}
// We have a valid, new event handler. Add it to the list.
// if listeners is null - we can automatically add this listener to the list at the beginning...
if (listeners == NULL)
{
listeners = newListener;
// if listeners is null - we can automatically add this listener to the list at the beginning...
if (listeners == NULL)
{
listeners = newListener;
MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
return MICROBIT_OK;
}
return MICROBIT_OK;
}
// We maintain an ordered list of listeners.
// The chain is held stictly in increasing order of ID (first level), then value code (second level).
// Find the correct point in the chain for this event.
// Adding a listener is a rare occurance, so we just walk the list...
// We maintain an ordered list of listeners.
// The chain is held stictly in increasing order of ID (first level), then value code (second level).
// Find the correct point in the chain for this event.
// Adding a listener is a rare occurance, so we just walk the list...
p = listeners;
l = listeners;
p = listeners;
l = listeners;
while (l != NULL && l->id < newListener->id)
{
p = l;
l = l->next;
}
while (l != NULL && l->id < newListener->id)
{
p = l;
l = l->next;
}
while (l != NULL && l->id == newListener->id && l->value < newListener->value)
{
p = l;
l = l->next;
}
while (l != NULL && l->id == newListener->id && l->value <= newListener->value)
{
p = l;
l = l->next;
}
//add at front of list
if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
{
newListener->next = p;
//add at front of list
if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
{
newListener->next = p;
//this new listener is now the front!
listeners = newListener;
}
//this new listener is now the front!
listeners = newListener;
}
//add after p
else
{
newListener->next = p->next;
p->next = newListener;
}
//add after p
else
{
newListener->next = p->next;
p->next = newListener;
}
MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
return MICROBIT_OK;
@ -482,14 +482,14 @@ int MicroBitMessageBus::add(MicroBitListener *newListener)
*/
int MicroBitMessageBus::remove(MicroBitListener *listener)
{
MicroBitListener *l;
MicroBitListener *l;
int removed = 0;
//handler can't be NULL!
if (listener == NULL)
return MICROBIT_INVALID_PARAMETER;
//handler can't be NULL!
if (listener == NULL)
return MICROBIT_INVALID_PARAMETER;
l = listeners;
l = listeners;
// Walk this list of event handlers. Delete any that match the given listener.
while (l != NULL)
@ -499,8 +499,12 @@ int MicroBitMessageBus::remove(MicroBitListener *listener)
if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
{
if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value) && (listener->cb_arg == l->cb_arg || listener->cb_arg == NULL))
{
// if notification of deletion has been requested, invoke the listener deletion callback.
if (listener_deletion_callback)
listener_deletion_callback(l);
// Found a match. mark this to be removed from the list.
l->flags |= MESSAGE_BUS_LISTENER_DELETING;
removed++;