Skip to content

Commit 008f95b

Browse files
committed
more attempts at the JS interrupt code - don't allow interruption of JS that's already interrupted
1 parent 5619232 commit 008f95b

File tree

7 files changed

+65
-44
lines changed

7 files changed

+65
-44
lines changed

src/jsinteractive.c

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "jsflash.h" // load and save to flash
2626
#include "jswrap_interactive.h" // jswrap_interactive_setTimeout
2727
#include "jswrap_object.h" // jswrap_object_keys_or_property_names
28+
#include "jswrap_timer.h" // jstOnRunInterruptJSEvent
2829
#include "jsnative.h" // jsnSanityTest
2930
#include "jswrap_storage.h" // for Packet Transfer IO
3031
#ifdef USE_FILESYSTEM
@@ -2204,21 +2205,6 @@ void jsiHandleIOEventForConsole(uint8_t *eventData, int eventLen) {
22042205
jsiSetBusy(BUSY_INTERACTIVE, false);
22052206
}
22062207

2207-
/** This is called if a EV_RUN_INTERRUPT_JS is received, or when a EXEC_RUN_INTERRUPT_JS is set.
2208-
It executes JavaScript code that was pushed to the queue by a require("timer").add({type:"EXEC", fn:myFunction... */
2209-
static void jsiOnRunInterruptJSEvent(const uint8_t *eventData, unsigned int eventLen) {
2210-
for (unsigned int i=0;i<eventLen;i++) {
2211-
uint8_t timerIdx = eventData[i];
2212-
JsVar *timerFns = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_TIMER_RUN_JS_NAME);
2213-
if (timerFns) {
2214-
JsVar *fn = jsvGetArrayItem(timerFns, timerIdx);
2215-
if (jsvIsFunction(fn))
2216-
jsvUnLock(jspExecuteFunction(fn, execInfo.root, 0, NULL));
2217-
jsvUnLock2(timerFns, fn);
2218-
}
2219-
}
2220-
}
2221-
22222208
void jsiIdle() {
22232209
// This is how many times we have been here and not done anything.
22242210
// It will be zeroed if we do stuff later
@@ -2264,7 +2250,7 @@ void jsiIdle() {
22642250
jsvUnLock(usartClass);
22652251
#endif
22662252
} else if (eventType == EV_RUN_INTERRUPT_JS) {
2267-
jsiOnRunInterruptJSEvent(eventData, eventLen);
2253+
jstOnRunInterruptJSEvent(eventData, eventLen);
22682254
} else if (eventType == EV_CUSTOM) {
22692255
jstOnCustomEvent(eventFlags, eventData, eventLen);
22702256
jswOnCustomEvent(eventFlags, eventData, eventLen);
@@ -3021,13 +3007,4 @@ void jsiDebuggerLine(JsVar *line) {
30213007
}
30223008
#endif // USE_DEBUGGER
30233009

3024-
/** This is called from the parser if EXEC_RUN_INTERRUPT_JS is set.
3025-
It executes JavaScript code that was pushed to the queue by require("timer").add({type:"EXEC", fn:myFunction... */
3026-
void jsiRunInterruptingJS() {
3027-
uint8_t data[IOEVENT_MAX_LEN];
3028-
memset(data, 0, sizeof(data));
3029-
unsigned int len;
3030-
if (jshPopIOEventOfType(EV_RUN_INTERRUPT_JS, data, &len))
3031-
jsiOnRunInterruptJSEvent(data, sizeof(data));
3032-
}
30333010

src/jsinteractive.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,4 @@ extern void jsiTimersChanged(); // Flag timers changed so we can skip out of the
202202
extern void jsiDebuggerLoop(); ///< Enter the debugger loop
203203
#endif
204204

205-
/** This is called from the parser if EXEC_RUN_INTERRUPT_JS is set.
206-
It executes JavaScript code that was pushed to the queue by require("timer").add({type:"EXEC", fn:myFunction... */
207-
void jsiRunInterruptingJS();
208-
209205
#endif /* JSINTERACTIVE_H_ */

src/jsparse.c

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "jswrap_json.h" // for jsfPrintJSON
2121
#include "jswrap_espruino.h" // for jswrap_espruino_memoryArea
2222
#include "jswrap_string.h" // for jswrap_string_charAt
23+
#include "jswrap_timer.h" // jstRunInterruptingJS
2324
#ifndef ESPR_NO_REGEX
2425
#include "jswrap_regexp.h" // for jswrap_regexp_constructor
2526
#endif
@@ -2951,17 +2952,22 @@ NO_INLINE JsVar *jspeStatementFunctionDecl(bool isClass) {
29512952
}
29522953

29532954
NO_INLINE JsVar *jspeStatement() {
2955+
if (execInfo.execute&(EXEC_RUN_INTERRUPT_JS
29542956
#ifdef USE_DEBUGGER
2955-
if (execInfo.execute&EXEC_DEBUGGER_NEXT_LINE &&
2956-
lex->tk!=';' &&
2957-
JSP_SHOULD_EXECUTE) {
2958-
lex->tokenLastStart = lex->tokenStart;
2959-
jsiDebuggerLoop();
2960-
}
2957+
|EXEC_DEBUGGER_NEXT_LINE
29612958
#endif
2962-
if (execInfo.execute&EXEC_RUN_INTERRUPT_JS) {
2963-
execInfo.execute&=~EXEC_RUN_INTERRUPT_JS;
2964-
jsiRunInterruptingJS();
2959+
)) {
2960+
#ifdef USE_DEBUGGER
2961+
if (execInfo.execute&EXEC_DEBUGGER_NEXT_LINE &&
2962+
lex->tk!=';' &&
2963+
JSP_SHOULD_EXECUTE) {
2964+
lex->tokenLastStart = lex->tokenStart;
2965+
jsiDebuggerLoop();
2966+
}
2967+
#endif
2968+
if (execInfo.execute&EXEC_RUN_INTERRUPT_JS) {
2969+
jstRunInterruptingJS();
2970+
}
29652971
}
29662972
if (lex->tk==LEX_ID ||
29672973
lex->tk==LEX_INT ||

src/jsparse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ typedef enum {
129129
EXEC_NO_PARSE_MASK = EXEC_INTERRUPTED|EXEC_ERROR, ///< in these cases we should exit as fast as possible - skipping out of parsing
130130
EXEC_SAVE_RESTORE_MASK = EXEC_YES|EXEC_BREAK|EXEC_CONTINUE|EXEC_RETURN|EXEC_IN_LOOP|EXEC_IN_SWITCH|EXEC_ERROR_MASK, ///< the things JSP_SAVE/RESTORE_EXECUTE should keep track of
131131
EXEC_CTRL_C_MASK = EXEC_CTRL_C | EXEC_CTRL_C_WAIT, ///< Ctrl-C was pressed at some point
132-
EXEC_PERSIST = EXEC_ERROR_MASK|EXEC_CTRL_C_MASK, ///< Things we should keep track of even after executing
132+
EXEC_PERSIST = EXEC_ERROR_MASK|EXEC_CTRL_C_MASK|EXEC_RUN_INTERRUPT_JS, ///< Things we should keep track of even after executing
133133
} JsExecFlags;
134134

135135
/** This structure is used when parsing the JavaScript. It contains

src/jstimer.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ bool utilTimerRemoveTask(int id) {
357357
// now we're at the last timer task - work back until we've just gone back past utilTimerTasksTail
358358
while (ptr != endPtr) {
359359
if (utilTimerTasks[ptr]==id) {
360+
utilTimerTaskInfo[utilTimerTasks[ptr]].type |= UET_FINISHED;
360361
// shift tail back along
361362
unsigned char next = (ptr+UTILTIMERTASK_TASKS-1) & (UTILTIMERTASK_TASKS-1);
362363
while (next!=endPtr) {
@@ -594,8 +595,8 @@ bool jstSetWakeUp(JsSysTime period) {
594595
nextTime = utilTimerTaskInfo[utilTimerTasks[utilTimerTasksTail]].time;
595596
}
596597
jshInterruptOn();
597-
598-
if (hasTimer && wakeupTime >= nextTime) {
598+
JsSysTime currTime = jshGetSystemTime();
599+
if (hasTimer && ((currTime+wakeupTime) >= (utilTimerSetTime+nextTime))) {
599600
// we already had a timer, and it's going to wake us up sooner.
600601
// don't create a WAKEUP timer task
601602
return true;

src/jswrap_timer.c

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,45 @@ require("timer").list()
3232
3333
This replaces `E.dumpTimers()` and `Pin.writeAtTime`
3434
*/
35+
volatile bool runningInterruptingJS = false;
3536

3637
static void jswrap_timer_queue_interrupt_js(JsSysTime time, void* userdata) {
3738
uint8_t timerIdx = (uint8_t)(size_t)userdata;
38-
jshPushIOCharEvents(EV_RUN_INTERRUPT_JS, (char*)&timerIdx, 1);
39-
execInfo.execute |= EXEC_RUN_INTERRUPT_JS;
40-
jshHadEvent();
39+
// if we were already running interrupting JS, don't interrupt it to run more!
40+
if (!runningInterruptingJS) {
41+
jshPushIOCharEvents(EV_RUN_INTERRUPT_JS, (char*)&timerIdx, 1);
42+
execInfo.execute |= EXEC_RUN_INTERRUPT_JS;
43+
jshHadEvent();
44+
}
45+
}
46+
47+
/** This is called if a EV_RUN_INTERRUPT_JS is received, or when a EXEC_RUN_INTERRUPT_JS is set.
48+
It executes JavaScript code that was pushed to the queue by a require("timer").add({type:"EXEC", fn:myFunction... */
49+
void jstOnRunInterruptJSEvent(const uint8_t *eventData, unsigned int eventLen) {
50+
runningInterruptingJS = true;
51+
execInfo.execute &= ~EXEC_RUN_INTERRUPT_JS;
52+
for (unsigned int i=0;i<eventLen;i++) {
53+
uint8_t timerIdx = eventData[i];
54+
JsVar *timerFns = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_TIMER_RUN_JS_NAME);
55+
if (timerFns) {
56+
JsVar *fn = jsvGetArrayItem(timerFns, timerIdx);
57+
if (jsvIsFunction(fn)) {
58+
jsvUnLock(jspExecuteFunction(fn, execInfo.root, 0, NULL));
59+
jsiCheckErrors(false); // check for any errors and report them
60+
}
61+
jsvUnLock2(timerFns, fn);
62+
}
63+
}
64+
runningInterruptingJS = false;
65+
}
66+
67+
/** This is called from the parser if EXEC_RUN_INTERRUPT_JS is set.
68+
It executes JavaScript code that was pushed to the queue by require("timer").add({type:"EXEC", fn:myFunction... */
69+
void jstRunInterruptingJS() {
70+
uint8_t data[IOEVENT_MAX_LEN];
71+
unsigned int len = 0;
72+
if (jshPopIOEventOfType(EV_RUN_INTERRUPT_JS, data, &len))
73+
jstOnRunInterruptJSEvent(data, len);
4174
}
4275

4376

src/jswrap_timer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@ JsVar *jswrap_timer_list();
2020
JsVar *jswrap_timer_get(int id);
2121
int jswrap_timer_add(JsVar *timer);
2222
void jswrap_timer_remove(int id);
23+
24+
/** This is called if a EV_RUN_INTERRUPT_JS is received, or when a EXEC_RUN_INTERRUPT_JS is set.
25+
It executes JavaScript code that was pushed to the queue by a require("timer").add({type:"EXEC", fn:myFunction... */
26+
void jstOnRunInterruptJSEvent(const uint8_t *eventData, unsigned int eventLen);
27+
28+
/** This is called from the parser if EXEC_RUN_INTERRUPT_JS is set.
29+
It executes JavaScript code that was pushed to the queue by require("timer").add({type:"EXEC", fn:myFunction... */
30+
void jstRunInterruptingJS();

0 commit comments

Comments
 (0)