Skip to content

Commit 9dedea4

Browse files
committed
Introduce optional javascript semantics for event handling
- new CONFIG option now allows the user to choose between ordered, serial processing of event handlers and parallel execution of events (JS semantics) OR parallel processing of event handlers and serial execution of events (legacy mciro:bit semantics) - Introduciton of a MicroBitLock primitive for mutual exclusion
1 parent 3898c0d commit 9dedea4

File tree

7 files changed

+197
-6
lines changed

7 files changed

+197
-6
lines changed

inc/core/MicroBitConfig.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,21 @@ extern uint32_t __etext;
171171
#define MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH 10
172172
#endif
173173

174+
//
175+
// Define MESSAGE_BUS concurrency behaviour.
176+
// Set to MESSAGE_BUS_CONCURRENT_LISTENERS to fire event handler
177+
// concurrently when a given event is raised, and process events sequentially as they arrive (default micro:bit semantics).
178+
// Set to MESSAGE_BUS_CONCURRENT_EVENTS to to fire event handlers sequentially for any given event, while still allowing
179+
// concurrent processing of events.
180+
//
181+
//
182+
// Permissable values are:
183+
// 0: MESSAGE_BUS_CONCURRENT_LISTENERS
184+
// 1: MESSAGE_BUS_CONCURRENT_EVENTS
185+
//
186+
#ifndef MESSAGE_BUS_CONCURRENCY_MODE
187+
#define MESSAGE_BUS_CONCURRENCY_MODE MESSAGE_BUS_CONCURRENT_LISTENERS
188+
#endif
174189
//
175190
// Core micro:bit services
176191
//

inc/core/MicroBitListener.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ DEALINGS IN THE SOFTWARE.
2828

2929
#include "mbed.h"
3030
#include "MicroBitConfig.h"
31+
#include "MicroBitLock.h"
3132
#include "MicroBitEvent.h"
3233
#include "MemberFunctionCallback.h"
33-
#include "MicroBitConfig.h"
3434

3535
// MicroBitListener flags...
3636
#define MESSAGE_BUS_LISTENER_PARAMETERISED 0x0001
@@ -67,7 +67,7 @@ struct MicroBitListener
6767

6868
MicroBitEvent evt;
6969
MicroBitEventQueueItem *evt_queue;
70-
70+
MicroBitLock lock;
7171
MicroBitListener *next;
7272

7373
/**

inc/core/MicroBitLock.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
The MIT License (MIT)
3+
4+
Copyright (c) 2016 British Broadcasting Corporation.
5+
This software is provided by Lancaster University by arrangement with the BBC.
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a
8+
copy of this software and associated documentation files (the "Software"),
9+
to deal in the Software without restriction, including without limitation
10+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
11+
and/or sell copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23+
DEALINGS IN THE SOFTWARE.
24+
*/
25+
26+
/**
27+
* A simple lock, mostly used for mutual exclusion.
28+
*/
29+
30+
#ifndef MICROBIT_LOCK_H
31+
#define MICROBIT_LOCK_H
32+
33+
#include "MicroBitConfig.h"
34+
35+
class Fiber;
36+
37+
class MicroBitLock
38+
{
39+
private:
40+
bool locked;
41+
Fiber *queue;
42+
43+
public:
44+
45+
/**
46+
* Create a new lock that can be used for mutual exclusion and condition synchronisation.
47+
*/
48+
MicroBitLock();
49+
50+
/**
51+
* Block the calling fiber until the lock is available
52+
**/
53+
void wait();
54+
55+
/**
56+
* Release the lock, and signal to one waiting fiber to continue
57+
*/
58+
void notify();
59+
60+
/**
61+
* Release the lock, and signal to all waiting fibers to continue
62+
*/
63+
void notifyAll();
64+
};
65+
66+
#endif

inc/drivers/MicroBitMessageBus.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ DEALINGS IN THE SOFTWARE.
3333
#include "MicroBitListener.h"
3434
#include "EventModel.h"
3535

36+
#define MESSAGE_BUS_CONCURRENT_LISTENERS 0
37+
#define MESSAGE_BUS_CONCURRENT_EVENTS 1
38+
3639
/**
3740
* Class definition for the MicroBitMessageBus.
3841
*
@@ -144,7 +147,6 @@ class MicroBitMessageBus : public EventModel, public MicroBitComponent
144147
MicroBitListener *listeners; // Chain of active listeners.
145148
MicroBitEventQueueItem *evt_queue_head; // Head of queued events to be processed.
146149
MicroBitEventQueueItem *evt_queue_tail; // Tail of queued events to be processed.
147-
uint16_t nonce_val; // The last nonce issued.
148150
uint16_t queueLength; // The number of events currently waiting to be processed.
149151

150152
/**

inc/platform/yotta_cfg_mappings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,8 @@
171171
#define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_DEVICE_INFO_SERVICE
172172
#endif
173173

174+
#ifdef YOTTA_CFG_MICROBIT_DAL_MESSAGE_BUS_CONCURRENCY_MODE
175+
#define MESSAGE_BUS_CONCURRENCY_MODE YOTTA_CFG_MICROBIT_DAL_MESSAGE_BUS_CONCURRENCY_MODE
176+
#endif
177+
174178
#endif

source/core/MicroBitFiber.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,3 +942,84 @@ void idle_task()
942942
schedule();
943943
}
944944
}
945+
946+
/**
947+
* Create a new lock that can be used for mutual exclusion and condition synchronisation.
948+
*/
949+
MicroBitLock::MicroBitLock()
950+
{
951+
queue = NULL;
952+
locked = false;
953+
}
954+
955+
/**
956+
* Block the calling fiber until the lock is available
957+
**/
958+
void MicroBitLock::wait()
959+
{
960+
Fiber *f = currentFiber;
961+
962+
// If the scheduler is not running, then simply exit, as we're running monothreaded.
963+
if (!fiber_scheduler_running())
964+
return;
965+
966+
if (locked)
967+
{
968+
// wait() is a blocking call, so if we're in a fork on block context,
969+
// it's time to spawn a new fiber...
970+
if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
971+
{
972+
// Allocate a new fiber. This will come from the fiber pool if availiable,
973+
// else a new one will be allocated on the heap.
974+
forkedFiber = getFiberContext();
975+
976+
// If we're out of memory, there's nothing we can do.
977+
// keep running in the context of the current thread as a best effort.
978+
if (forkedFiber != NULL)
979+
f = forkedFiber;
980+
}
981+
982+
// Remove fiber from the run queue
983+
dequeue_fiber(f);
984+
985+
// Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times.
986+
queue_fiber(f, &queue);
987+
988+
// Finally, enter the scheduler.
989+
schedule();
990+
}
991+
992+
locked = true;
993+
}
994+
995+
/**
996+
* Release the lock, and signal to one waiting fiber to continue
997+
*/
998+
void MicroBitLock::notify()
999+
{
1000+
Fiber *f = queue;
1001+
1002+
if (f)
1003+
{
1004+
dequeue_fiber(f);
1005+
queue_fiber(f, &runQueue);
1006+
}
1007+
locked = false;
1008+
}
1009+
1010+
/**
1011+
* Release the lock, and signal to all waiting fibers to continue
1012+
*/
1013+
void MicroBitLock::notifyAll()
1014+
{
1015+
Fiber *f = queue;
1016+
1017+
while (f)
1018+
{
1019+
dequeue_fiber(f);
1020+
queue_fiber(f, &runQueue);
1021+
f = queue;
1022+
}
1023+
1024+
locked = false;
1025+
}

source/drivers/MicroBitMessageBus.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,19 @@ void async_callback(void *param)
9595
// Queue this event up for later, if that's how we've been configured.
9696
if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
9797
{
98+
#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_LISTENERS)
9899
listener->queue(listener->evt);
99100
return;
101+
#endif
100102
}
101103
}
102104

103105
// Determine the calling convention for the callback, and invoke...
104106
// C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
105107

108+
#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_EVENTS)
109+
listener->lock.wait();
110+
#endif
106111
// Record that we have a fiber going into this listener...
107112
listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
108113

@@ -138,6 +143,10 @@ void async_callback(void *param)
138143

139144
// The fiber of exiting... clear our state.
140145
listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
146+
147+
#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_EVENTS)
148+
listener->lock.notify();
149+
#endif
141150
}
142151

143152
/**
@@ -261,6 +270,13 @@ int MicroBitMessageBus::deleteMarkedListeners()
261270
return removed;
262271
}
263272

273+
MicroBitEvent last_event;
274+
void process_sequentially(void *param)
275+
{
276+
MicroBitMessageBus *m = (MicroBitMessageBus *)param;
277+
m->process(last_event);
278+
}
279+
264280
/**
265281
* Periodic callback from MicroBit.
266282
*
@@ -278,7 +294,12 @@ void MicroBitMessageBus::idleTick()
278294
while (item)
279295
{
280296
// send the event to all standard event listeners.
297+
#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_EVENTS)
298+
last_event = item->evt;
299+
invoke(process_sequentially,this);
300+
#else
281301
this->process(item->evt);
302+
#endif
282303

283304
// Free the queue item.
284305
delete item;
@@ -365,10 +386,12 @@ int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
365386
// Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
366387
// should the event handler attempt a blocking operation, but doesn't have the overhead
367388
// of creating a fiber needlessly. (cool huh?)
368-
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running())
369-
async_callback(l);
370-
else
389+
#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_LISTENERS)
390+
if (!(l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING) && fiber_scheduler_running())
371391
invoke(async_callback, l);
392+
else
393+
#endif
394+
async_callback(l);
372395
}
373396
else
374397
{

0 commit comments

Comments
 (0)