11import { TargetType } from '~/services/config' ;
22import { getWorkerInstance } from "~/services/gorepl" ;
33import { getImportObject , goRun } from '~/services/go' ;
4- import { setTimeoutNanos } from "~/utils/duration" ;
4+ import { setTimeoutNanos , SECOND } from "~/utils/duration" ;
55import client , {
66 EvalEvent ,
77 EvalEventKind ,
@@ -29,6 +29,29 @@ import {wrapResponseWithProgress} from "~/utils/http";
2929const WASM_APP_DOWNLOAD_NOTIFICATION = 'WASM_APP_DOWNLOAD_NOTIFICATION' ;
3030const WASM_APP_EXIT_ERROR = 'WASM_APP_EXIT_ERROR' ;
3131
32+ /**
33+ * Go program execution timeout in nanoseconds
34+ */
35+ const runTimeoutNs = 5 * SECOND ;
36+
37+ const lastElem = < T > ( items : T [ ] ) : T | undefined => (
38+ items ?. slice ( - 1 ) ?. [ 0 ]
39+ ) ;
40+
41+ const hasProgramTimeoutError = ( events : EvalEvent [ ] ) => {
42+ if ( ! events . length ) {
43+ return false ;
44+ }
45+
46+ const { Message, Kind } = events [ 0 ] ;
47+ if ( Kind === 'stderr' && Message . trim ( ) === 'timeout running program' ) {
48+ const lastEvent = lastElem ( events ) ;
49+ return lastEvent ! . Delay >= runTimeoutNs ;
50+ }
51+
52+ return false ;
53+ }
54+
3255const dispatchEvalEvents = ( dispatch : DispatchFn , events : EvalEvent [ ] ) => {
3356 // TODO: support cancellation
3457
@@ -39,20 +62,37 @@ const dispatchEvalEvents = (dispatch: DispatchFn, events: EvalEvent[]) => {
3962
4063 // Each eval event contains time since previous event.
4164 // Convert relative delay into absolute delay since program start.
42- const eventsWithDelay = events . map ( ( event , i , arr ) => (
43- i === 0 ? event : (
44- {
45- ...event ,
46- Delay : arr [ i - 1 ] . Delay + event . Delay
47- }
48- )
49- ) ) ;
65+ let eventsWithDelay = events
66+ . reduce ( ( accum : EvalEvent [ ] , { Delay : delay , ...item } ) => (
67+ [
68+ ...accum ,
69+ {
70+ ...item ,
71+ Delay : ( lastElem ( accum ) ?. Delay ?? 0 ) + delay ,
72+ }
73+ ]
74+ ) , [ ] ) ;
75+
76+ // Sometimes Go playground fails to detect execution timeout error and still sends all events.
77+ // This dirty hack attempts to normalize this case.
78+ if ( hasProgramTimeoutError ( eventsWithDelay ) ) {
79+ eventsWithDelay = eventsWithDelay
80+ . slice ( 1 )
81+ . filter ( ( { Delay} ) => Delay <= runTimeoutNs )
82+ . concat ( {
83+ Kind : EvalEventKind . Stderr ,
84+ Message : `Go program execution timeout exceeded (max: ${ runTimeoutNs / SECOND } s)` ,
85+ Delay : runTimeoutNs ,
86+ } ) ;
87+ }
88+
89+ console . log ( eventsWithDelay ) ;
5090
5191 // Try to guess program end time by checking last message delay.
5292 //
5393 // This won't work if "time.Sleep()" occurs after final message but the same
5494 // approach used in official playground, so should be enough for us.
55- const programEndTime = eventsWithDelay ?. slice ( - 1 ) ?. [ 0 ] ?. Delay ?? 0 ;
95+ let programEndTime = lastElem ( eventsWithDelay ) ?. Delay ?? 0 ;
5696
5797 dispatch ( newProgramStartAction ( ) ) ;
5898 eventsWithDelay . forEach ( event => {
0 commit comments