Skip to content

Commit 585d5d3

Browse files
committed
Swap to using locks for watch&timer arrays (uses 4b more RAM, but faster and no defrag issues)
1 parent a675619 commit 585d5d3

File tree

5 files changed

+34
-75
lines changed

5 files changed

+34
-75
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Fix jshPopIOEventOfType when the event to be popped is right at the start
1313
STM32: Tidy up WAKEUP timer handling to ensure the Wakeup is always the first item in the queue
1414
E.defrag now defrags with interrupts enabled (also fix potential crash with 2 flat strings end to end)
15+
Swap to using locks for watch&timer arrays (uses 4b more RAM, but faster and no defrag issues)
1516

1617
2v28 : Add `E.internal` as a way to access the 'hidden root' containing Espruino internal variables that previously needed `global["\xff"]`
1718
Bangle.js: Fix back handler not removed when using E.setUI with a back button but without widgets (#2636)

src/jsinteractive.c

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ Espruino.Core.Serial.write("\x10\x01\x80\x0Bhello world")
107107
#define ASCII_SOH (1)
108108

109109
JsVar *events = 0; // Array of events to execute
110-
JsVarRef timerArray = 0; // Linked List of timers to check and run
111-
JsVarRef watchArray = 0; // Linked List of input watches to check and run
110+
JsVar *timerArray = 0; // Linked List of timers to check and run
111+
JsVar *watchArray = 0; // Linked List of input watches to check and run
112112
// ----------------------------------------------------------------------------
113113
IOEventFlags consoleDevice = DEFAULT_CONSOLE_DEVICE; ///< The console device for user interaction
114114
#ifndef SAVE_ON_FLASH
@@ -488,14 +488,6 @@ void jsiSetSleep(JsiSleepType isSleep) {
488488
#endif
489489
}
490490

491-
static JsVarRef _jsiInitNamedArray(const char *name) {
492-
JsVar *array = jsvObjectGetChild(execInfo.hiddenRoot, name, JSV_ARRAY);
493-
JsVarRef arrayRef = 0;
494-
if (array) arrayRef = jsvGetRef(jsvRef(array));
495-
jsvUnLock(array);
496-
return arrayRef;
497-
}
498-
499491
// Used when recovering after being flashed
500492
// 'claim' anything we are using
501493
void jsiSoftInit(bool hasBeenReset) {
@@ -514,8 +506,8 @@ void jsiSoftInit(bool hasBeenReset) {
514506
#endif
515507

516508
// Load timer/watch arrays
517-
timerArray = _jsiInitNamedArray(JSI_TIMERS_NAME);
518-
watchArray = _jsiInitNamedArray(JSI_WATCHES_NAME);
509+
timerArray = jsvObjectGetChild(execInfo.hiddenRoot, JSI_TIMERS_NAME, JSV_ARRAY);
510+
watchArray = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WATCHES_NAME, JSV_ARRAY);
519511

520512
// Make sure we set up lastIdleTime, as this could be used
521513
// when adding an interval from onInit (called below)
@@ -548,9 +540,8 @@ void jsiSoftInit(bool hasBeenReset) {
548540

549541
// Check any existing watches and set up interrupts for them
550542
if (watchArray) {
551-
JsVar *watchArrayPtr = jsvLock(watchArray);
552543
JsvObjectIterator it;
553-
jsvObjectIteratorNew(&it, watchArrayPtr);
544+
jsvObjectIteratorNew(&it, watchArray);
554545
while (jsvObjectIteratorHasValue(&it)) {
555546
JsVar *watch = jsvObjectIteratorGetValue(&it);
556547
JsVar *watchPin = jsvObjectGetChildIfExists(watch, "pin");
@@ -560,7 +551,6 @@ void jsiSoftInit(bool hasBeenReset) {
560551
jsvObjectIteratorNext(&it);
561552
}
562553
jsvObjectIteratorFree(&it);
563-
jsvUnLock(watchArrayPtr);
564554
}
565555

566556
// Timers are stored by time in the future now, so no need
@@ -807,14 +797,13 @@ void jsiSoftKill() {
807797
events=0;
808798
}
809799
if (timerArray) {
810-
jsvUnRefRef(timerArray);
800+
jsvUnLock(timerArray);
811801
timerArray=0;
812802
}
813803
if (watchArray) {
814804
// Check any existing watches and disable interrupts for them
815-
JsVar *watchArrayPtr = jsvLock(watchArray);
816805
JsvObjectIterator it;
817-
jsvObjectIteratorNew(&it, watchArrayPtr);
806+
jsvObjectIteratorNew(&it, watchArray);
818807
while (jsvObjectIteratorHasValue(&it)) {
819808
JsVar *watchPtr = jsvObjectIteratorGetValue(&it);
820809
JsVar *watchPin = jsvObjectGetChildIfExists(watchPtr, "pin");
@@ -823,8 +812,7 @@ void jsiSoftKill() {
823812
jsvObjectIteratorNext(&it);
824813
}
825814
jsvObjectIteratorFree(&it);
826-
jsvUnRef(watchArrayPtr);
827-
jsvUnLock(watchArrayPtr);
815+
jsvUnLock(watchArray);
828816
watchArray=0;
829817
}
830818
// Save flags if required
@@ -2136,11 +2124,7 @@ void jsiClearTimeout(JsVar *timeout) {
21362124
}
21372125

21382126
bool jsiHasTimers() {
2139-
if (!timerArray) return false;
2140-
JsVar *timerArrayPtr = jsvLock(timerArray);
2141-
bool hasTimers = !jsvArrayIsEmpty(timerArrayPtr);
2142-
jsvUnLock(timerArrayPtr);
2143-
return hasTimers;
2127+
return !jsvArrayIsEmpty(timerArray);
21442128
}
21452129

21462130
/// Is the given watch object meant to be executed when the current value of the pin is pinIsHigh
@@ -2155,9 +2139,8 @@ bool jsiIsWatchingPin(Pin pin) {
21552139
if (jshGetPinShouldStayWatched(pin))
21562140
return true;
21572141
bool isWatched = false;
2158-
JsVar *watchArrayPtr = jsvLock(watchArray);
21592142
JsvObjectIterator it;
2160-
jsvObjectIteratorNew(&it, watchArrayPtr);
2143+
jsvObjectIteratorNew(&it, watchArray);
21612144
while (jsvObjectIteratorHasValue(&it)) {
21622145
JsVar *watchPtr = jsvObjectIteratorGetValue(&it);
21632146
JsVar *pinVar = jsvObjectGetChildIfExists(watchPtr, "pin");
@@ -2167,7 +2150,6 @@ bool jsiIsWatchingPin(Pin pin) {
21672150
jsvObjectIteratorNext(&it);
21682151
}
21692152
jsvObjectIteratorFree(&it);
2170-
jsvUnLock(watchArrayPtr);
21712153
return isWatched;
21722154
}
21732155

@@ -2283,9 +2265,8 @@ void jsiIdle() {
22832265
} else if (DEVICE_IS_EXTI(eventType)) { // ---------------------------------------------------------------- PIN WATCH
22842266
// we have an event... find out what it was for...
22852267
// Check everything in our Watch array
2286-
JsVar *watchArrayPtr = jsvLock(watchArray);
22872268
JsvObjectIterator it;
2288-
jsvObjectIteratorNew(&it, watchArrayPtr);
2269+
jsvObjectIteratorNew(&it, watchArray);
22892270
while (jsvObjectIteratorHasValue(&it)) {
22902271
bool hasDeletedWatch = false;
22912272
JsVar *watchPtr = jsvObjectIteratorGetValue(&it);
@@ -2385,7 +2366,7 @@ void jsiIdle() {
23852366
jsvUnLock(data);
23862367
if (!watchRecurring) {
23872368
// free all
2388-
jsvObjectIteratorRemoveAndGotoNext(&it, watchArrayPtr);
2369+
jsvObjectIteratorRemoveAndGotoNext(&it, watchArray);
23892370
hasDeletedWatch = true;
23902371
if (!jsiIsWatchingPin(pin))
23912372
jshPinWatch(pin, false, JSPW_NONE);
@@ -2401,7 +2382,6 @@ void jsiIdle() {
24012382
jsvObjectIteratorNext(&it);
24022383
}
24032384
jsvObjectIteratorFree(&it);
2404-
jsvUnLock(watchArrayPtr);
24052385
}
24062386
}
24072387

@@ -2423,10 +2403,9 @@ void jsiIdle() {
24232403
jsiTimeSinceCtrlC = 0xFFFFFFFF;
24242404
#endif
24252405

2426-
JsVar *timerArrayPtr = jsvLock(timerArray);
24272406
JsvObjectIterator it;
24282407
// Go through all intervals and decrement time
2429-
jsvObjectIteratorNew(&it, timerArrayPtr);
2408+
jsvObjectIteratorNew(&it, timerArray);
24302409
while (jsvObjectIteratorHasValue(&it)) {
24312410
JsVar *timerPtr = jsvObjectIteratorGetValue(&it);
24322411
JsSysTime timerTime = (JsSysTime)jsvGetLongIntegerAndUnLock(jsvObjectGetChildIfExists(timerPtr, "time"));
@@ -2439,7 +2418,7 @@ void jsiIdle() {
24392418
// Now go through intervals and execute if needed
24402419
do {
24412420
jsiStatus = jsiStatus & ~JSIS_TIMERS_CHANGED;
2442-
jsvObjectIteratorNew(&it, timerArrayPtr);
2421+
jsvObjectIteratorNew(&it, timerArray);
24432422
while (jsvObjectIteratorHasValue(&it) && !(jsiStatus & JSIS_TIMERS_CHANGED)) {
24442423
bool hasDeletedTimer = false;
24452424
JsVar *timerPtr = jsvObjectIteratorGetValue(&it);
@@ -2507,12 +2486,10 @@ void jsiIdle() {
25072486
if (exec) {
25082487
bool watchRecurring = jsvObjectGetBoolChild(watchPtr, "recur");
25092488
if (!watchRecurring) {
2510-
JsVar *watchArrayPtr = jsvLock(watchArray);
2511-
JsVar *watchNamePtr = jsvGetIndexOf(watchArrayPtr, watchPtr, true);
2489+
JsVar *watchNamePtr = jsvGetIndexOf(watchArray, watchPtr, true);
25122490
if (watchNamePtr) {
2513-
jsvRemoveChildAndUnLock(watchArrayPtr, watchNamePtr);
2491+
jsvRemoveChildAndUnLock(watchArray, watchNamePtr);
25142492
}
2515-
jsvUnLock(watchArrayPtr);
25162493
Pin pin = jshGetPinFromVarAndUnLock(jsvObjectGetChildIfExists(watchPtr, "pin"));
25172494
if (!jsiIsWatchingPin(pin))
25182495
jshPinWatch(pin, false, JSPW_NONE);
@@ -2528,7 +2505,7 @@ void jsiIdle() {
25282505
} else {
25292506
// free
25302507
// Beware... may have already been removed!
2531-
jsvObjectIteratorRemoveAndGotoNext(&it, timerArrayPtr);
2508+
jsvObjectIteratorRemoveAndGotoNext(&it, timerArray);
25322509
hasDeletedTimer = true;
25332510
timerTime = -1;
25342511
}
@@ -2544,7 +2521,6 @@ void jsiIdle() {
25442521
}
25452522
jsvObjectIteratorFree(&it);
25462523
} while (jsiStatus & JSIS_TIMERS_CHANGED);
2547-
jsvUnLock(timerArrayPtr);
25482524
/* We might have left the timers loop with stuff to do because the contents of it
25492525
* changed. It's not a big deal because it could only have changed because a timer
25502526
* got executed - so `wasBusy` got set and we know we're going to go around the
@@ -2755,9 +2731,7 @@ void jsiDumpState(vcbprintf_callback user_callback, void *user_data) {
27552731
}
27562732
jsvObjectIteratorFree(&it);
27572733
// Now do timers
2758-
JsVar *timerArrayPtr = jsvLock(timerArray);
2759-
jsvObjectIteratorNew(&it, timerArrayPtr);
2760-
jsvUnLock(timerArrayPtr);
2734+
jsvObjectIteratorNew(&it, timerArray);
27612735
while (jsvObjectIteratorHasValue(&it)) {
27622736
JsVar *timer = jsvObjectIteratorGetValue(&it);
27632737
JsVar *timerNumber = jsvObjectIteratorGetKey(&it);
@@ -2773,9 +2747,7 @@ void jsiDumpState(vcbprintf_callback user_callback, void *user_data) {
27732747
}
27742748
jsvObjectIteratorFree(&it);
27752749
// Now do watches
2776-
JsVar *watchArrayPtr = jsvLock(watchArray);
2777-
jsvObjectIteratorNew(&it, watchArrayPtr);
2778-
jsvUnLock(watchArrayPtr);
2750+
jsvObjectIteratorNew(&it, watchArray);
27792751
while (jsvObjectIteratorHasValue(&it)) {
27802752
JsVar *watch = jsvObjectIteratorGetValue(&it);
27812753
JsVar *watchCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromString(watch, "cb"));
@@ -2811,10 +2783,7 @@ void jsiDumpState(vcbprintf_callback user_callback, void *user_data) {
28112783
}
28122784

28132785
JsVarInt jsiTimerAdd(JsVar *timerPtr) {
2814-
JsVar *timerArrayPtr = jsvLock(timerArray);
2815-
JsVarInt itemIndex = jsvArrayAddToEnd(timerArrayPtr, timerPtr, 1) - 1;
2816-
jsvUnLock(timerArrayPtr);
2817-
return itemIndex;
2786+
return jsvArrayAddToEnd(timerArray, timerPtr, 1) - 1;
28182787
}
28192788

28202789
void jsiTimersChanged() {

src/jsinteractive.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ void jsiDumpJSON(vcbprintf_callback user_callback, void *user_data, JsVar *data,
191191
void jsiDumpState(vcbprintf_callback user_callback, void *user_data);
192192
#define TIMER_MIN_INTERVAL 0.1 // in milliseconds
193193
#define TIMER_MAX_INTERVAL 31536000001000ULL // in milliseconds
194-
extern JsVarRef timerArray; // Linked List of timers to check and run
195-
extern JsVarRef watchArray; // Linked List of input watches to check and run
194+
extern JsVar *timerArray; // Linked List of timers to check and run
195+
extern JsVar *watchArray; // Linked List of input watches to check and run
196196

197197
extern JsVarInt jsiTimerAdd(JsVar *timerPtr);
198198
extern void jsiTimersChanged(); // Flag timers changed so we can skip out of the loop if needed

src/jswrap_interactive.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -537,17 +537,16 @@ If no argument is supplied, all timeouts and intervals are stopped.
537537
To avoid accidentally deleting all Timeouts, if a parameter is supplied but is `undefined` then an Exception will be thrown.
538538
*/
539539
void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVarArr, bool isTimeout) {
540-
JsVar *timerArrayPtr = jsvLock(timerArray);
541540
if (jsvIsUndefined(idVarArr) || jsvGetArrayLength(idVarArr)==0) {
542541
/* Delete all timers EXCEPT those with a 'watch' field,
543542
as those were generated by jsinteractive.c for debouncing watches */
544543
JsvObjectIterator it;
545-
jsvObjectIteratorNew(&it, timerArrayPtr);
544+
jsvObjectIteratorNew(&it, timerArray);
546545
while (jsvObjectIteratorHasValue(&it)) {
547546
JsVar *timerPtr = jsvObjectIteratorGetValue(&it);
548547
JsVar *watchPtr = jsvObjectGetChildIfExists(timerPtr, "watch");
549548
if (!watchPtr)
550-
jsvObjectIteratorRemoveAndGotoNext(&it, timerArrayPtr);
549+
jsvObjectIteratorRemoveAndGotoNext(&it, timerArray);
551550
else
552551
jsvObjectIteratorNext(&it);
553552
jsvUnLock2(watchPtr, timerPtr);
@@ -559,13 +558,12 @@ void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVarArr, bool isTimeout) {
559558
const char *name = isTimeout?"Timeout":"Interval";
560559
jsExceptionHere(JSET_ERROR, "clear%s(undefined) not allowed. Use clear%s() instead", name, name);
561560
} else {
562-
JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArrayPtr, idVar, false) : 0;
561+
JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArray, idVar, false) : 0;
563562
if (child)
564-
jsvRemoveChildAndUnLock(timerArrayPtr, child);
563+
jsvRemoveChildAndUnLock(timerArray, child);
565564
jsvUnLock(idVar);
566565
}
567566
}
568-
jsvUnLock(timerArrayPtr);
569567
jsiTimersChanged(); // mark timers as changed
570568
}
571569
void jswrap_interface_clearInterval(JsVar *idVarArr) {
@@ -596,9 +594,8 @@ regardless of when you call `changeInterval`, the next interval will occur
596594
1500ms after it.
597595
*/
598596
void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval) {
599-
JsVar *timerArrayPtr = jsvLock(timerArray);
600597
if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
601-
JsVar *timerName = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArrayPtr, idVar, false) : 0;
598+
JsVar *timerName = jsvIsBasic(idVar) ? jsvFindChildFromVar(timerArray, idVar, false) : 0;
602599
if (timerName) {
603600
JsVar *timer = jsvSkipNameAndUnLock(timerName);
604601
JsSysTime intervalInt = jshGetTimeFromMilliseconds(interval);
@@ -610,5 +607,4 @@ void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval) {
610607
} else {
611608
jsExceptionHere(JSET_ERROR, "Unknown Interval");
612609
}
613-
jsvUnLock(timerArrayPtr);
614610
}

src/jswrap_io.c

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -855,9 +855,8 @@ JsVar *jswrap_interface_setWatch(
855855
}
856856

857857

858-
JsVar *watchArrayPtr = jsvLock(watchArray);
859-
itemIndex = jsvArrayAddToEnd(watchArrayPtr, watchPtr, 1) - 1;
860-
jsvUnLock2(watchArrayPtr, watchPtr);
858+
itemIndex = jsvArrayAddToEnd(watchArray, watchPtr, 1) - 1;
859+
jsvUnLock(watchPtr);
861860

862861

863862
}
@@ -882,9 +881,8 @@ To avoid accidentally deleting all Watches, if a parameter is supplied but is `u
882881
*/
883882
void jswrap_interface_clearWatch(JsVar *idVarArr) {
884883
if (jsvIsUndefined(idVarArr) || jsvGetArrayLength(idVarArr)==0) {
885-
JsVar *watchArrayPtr = jsvLock(watchArray);
886884
JsvObjectIterator it;
887-
jsvObjectIteratorNew(&it, watchArrayPtr);
885+
jsvObjectIteratorNew(&it, watchArray);
888886
while (jsvObjectIteratorHasValue(&it)) {
889887
JsVar *watchPtr = jsvObjectIteratorGetValue(&it);
890888
JsVar *watchPin = jsvObjectGetChildIfExists(watchPtr, "pin");
@@ -896,25 +894,20 @@ void jswrap_interface_clearWatch(JsVar *idVarArr) {
896894
}
897895
jsvObjectIteratorFree(&it);
898896
// remove all items
899-
jsvRemoveAllChildren(watchArrayPtr);
900-
jsvUnLock(watchArrayPtr);
897+
jsvRemoveAllChildren(watchArray);
901898
} else {
902899
JsVar *idVar = jsvGetArrayItem(idVarArr, 0);
903900
if (jsvIsUndefined(idVar)) {
904901
jsExceptionHere(JSET_ERROR, "clearWatch(undefined) not allowed. Use clearWatch() instead");
905902
return;
906903
}
907-
JsVar *watchArrayPtr = jsvLock(watchArray);
908-
JsVar *watchNamePtr = jsvFindChildFromVar(watchArrayPtr, idVar, false);
909-
jsvUnLock(watchArrayPtr);
904+
JsVar *watchNamePtr = jsvFindChildFromVar(watchArray, idVar, false);
910905
if (watchNamePtr) { // child is a 'name'
911906
JsVar *watchPtr = jsvSkipName(watchNamePtr);
912907
Pin pin = jshGetPinFromVarAndUnLock(jsvObjectGetChildIfExists(watchPtr, "pin"));
913908
jsvUnLock(watchPtr);
914909

915-
JsVar *watchArrayPtr = jsvLock(watchArray);
916-
jsvRemoveChildAndUnLock(watchArrayPtr, watchNamePtr);
917-
jsvUnLock(watchArrayPtr);
910+
jsvRemoveChildAndUnLock(watchArray, watchNamePtr);
918911

919912
// Now check if this pin is still being watched
920913
if (!jsiIsWatchingPin(pin))

0 commit comments

Comments
 (0)