From: stephan Date: Sat, 21 May 2022 14:19:05 +0000 (+0000) Subject: fiddle: refactor into main thread (UI) and worker thread (wasm module). Added bits... X-Git-Tag: version-3.39.0~124 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4257373f6d0598ccc6a9262be1ba7c678a6a7a48;p=thirdparty%2Fsqlite.git fiddle: refactor into main thread (UI) and worker thread (wasm module). Added bits needed to support triggering sqlite3_interrupt() but do not yet have a second SharedWorker to test it with. FossilOrigin-Name: 5ff3326856bc190cee15a5fca5ded89aacc4bf931a8df98726a872b310e2a4fc --- diff --git a/Makefile.in b/Makefile.in index a14c6a87ee..7a90bb5f0c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1519,9 +1519,9 @@ sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def # fiddle_dir = ext/fiddle fiddle_html = $(fiddle_dir)/fiddle.html -fiddle_generated = $(fiddle_html) \ - $(fiddle_dir)/fiddle.js \ - $(fiddle_dir)/fiddle.wasm +fiddle_module_js = $(fiddle_dir)/fiddle-module.js +fiddle_generated = $(fiddle_module_js) \ + $(fiddle_dir)/fiddle-module.wasm # fiddle_dummy_exports = ,comma,list of func bound solely for # experimentation and testing purposes in the WASM build. fiddle_dummy_exports = ,_fiddle_experiment,_fiddle_the_db,_fiddle_db_arg @@ -1536,15 +1536,10 @@ clean: clean-fiddle emcc_opt = -Oz emcc_flags = $(emcc_opt) $(SHELL_OPT) \ -sEXPORTED_RUNTIME_METHODS=ccall,cwrap \ - -sEXPORTED_FUNCTIONS=_fiddle_exec$(fiddle_dummy_exports) \ - --pre-js $(fiddle_dir)/module-pre.js \ - --post-js $(fiddle_dir)/module-post.js \ - --shell-file $(fiddle_dir)/fiddle.in.html \ + -sEXPORTED_FUNCTIONS=_fiddle_exec,_fiddle_interrupt$(fiddle_dummy_exports) \ $(fiddle_cflags) # $(fiddle_cflags) is intended to be passed to make via the CLI in # order to override, e.g., -Ox for one-off builds. -$(fiddle_html): Makefile sqlite3.c shell.c \ - $(fiddle_dir)/fiddle.in.html \ - $(fiddle_dir)/module-pre.js $(fiddle_dir)/module-post.js +$(fiddle_module_js): Makefile sqlite3.c shell.c emcc -o $@ $(emcc_flags) sqlite3.c shell.c -fiddle: $(fiddle_html) +fiddle: $(fiddle_module_js) diff --git a/ext/fiddle/fiddle-worker.js b/ext/fiddle/fiddle-worker.js new file mode 100644 index 0000000000..5c6ed1d483 --- /dev/null +++ b/ext/fiddle/fiddle-worker.js @@ -0,0 +1,141 @@ +/* + 2022-05-20 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + This is the JS Worker file for the sqlite3 fiddle app. It loads the + sqlite3 wasm module and offers access to the db via the Worker + message-passing interface. + + Because we can have only a single message handler, as opposed to an + arbitrary number of discrete event listeners like with DOM elements, + we have to define a lower-level message API. Messages abstractly + look like: + + { type: string, data: type-specific value } + + Where 'type' is used for dispatching and 'data' is a + 'type'-dependent value. + + The 'type' values expected by each side of the main/worker + connection vary. The types are described below but subject to + change at any time as this experiment evolves. + + Workers-to-Main types + + - stdout, stderr: indicate stdout/stderr output from the wasm + layer. The data property is the string of the output, noting + that the emscripten binding emits these one line at a time. Thus, + if a C-side puts() emits multiple lines in a single call, the JS + side will see that as multiple calls. + + - module: Status text. This is intended to alert the main thread + about module loading status so that, e.g., the main thread can + update a progress widget and DTRT when the module is finished + loading and available for work. The status text is mostly in some + undocumented(?) format emited by the emscripten generated + module-loading code, encoding progress info within it. + + - working: data='start'|'end'. Indicates that work is about to be + sent to the module or has just completed. This can be used, e.g., + to disable UI elements which should not be activated while work + is pending. + + Main-to-Worker types: + + - shellExec: data=text to execute as if it had been entered in the + sqlite3 CLI shell app (as opposed to sqlite3_exec()). This event + causes the worker to emit a 'working' event (data='start') before + it starts and a 'working' event (data='end') when it finished. If + called while work is currently being executed it emits stderr + message instead of doing actual work, as the underlying db cannot + handle concurrent tasks. + + - More TBD as the higher-level db layer develops. +*/ + +/* + Apparent browser(s) bug: console messages emitted may be duplicated + in the console, even though they're provably only run once. See: + + https://stackoverflow.com/questions/49659464 + + Noting that it happens in Firefox as well as Chrome. Harmless but + annoying. +*/ + +const thisWorker = self; + +const wMsg = (type,data)=>postMessage({type, data}); + +self.onerror = function(/*message, source, lineno, colno, error*/) { + const err = arguments[4]; + if(err && 'ExitStatus'==err.name){ + Module._isDead = true; + Module.printErr("FATAL ERROR:", err.message); + Module.printErr("Restarting the app requires reloading the page."); + //const taOutput = document.querySelector('#output'); + //if(taOutput) taOutput.classList.add('error'); + } + Module.setStatus('Exception thrown, see JavaScript console'); + Module.setStatus = function(text) { + if(text) console.error('[post-exception status] ' + text); + }; +}; + +self.Module = { +/* ^^^ cannot declare that const because fiddle-module.js + (auto-generated) includes a decl for it and runs in this scope. */ + preRun: [], + postRun: [], + //onRuntimeInitialized: function(){}, + print: function(text){wMsg('stdout', Array.prototype.slice.call(arguments));}, + printErr: function(text){wMsg('stderr', Array.prototype.slice.call(arguments));}, + setStatus: function f(text){wMsg('module',{type:'status',data:text});}, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + this.setStatus(left + ? ('Preparing... (' + (this.totalDependencies-left) + + '/' + this.totalDependencies + ')') + : 'All downloads complete.'); + } +}; + +const shellExec = function f(sql){ + if(!f._) f._ = Module.cwrap('fiddle_exec', null, ['string']); + if(Module._isDead){ + wMsg('stderr', "shell module has exit()ed. Cannot run SQL."); + return; + } + wMsg('working','start'); + try { + if(f._running) wMsg('stderr','Cannot run multiple commands concurrently.'); + else{ + f._running = true; + f._(sql); + } + } finally { + wMsg('working','end'); + delete f._running; + } +}; + +self.onmessage = function(ev){ + ev = ev.data; + //console.debug("worker: onmessage.data",ev); + switch(ev.type){ + case 'shellExec': shellExec(ev.data); return; + }; + console.warn("Unknown fiddle-worker message type:",ev); +}; +self.Module.setStatus('Downloading...'); +importScripts('fiddle-module.js') +/* loads module and notifies, via Module.setStatus(), when it's done loading. */; diff --git a/ext/fiddle/fiddle.in.html b/ext/fiddle/fiddle.html similarity index 88% rename from ext/fiddle/fiddle.in.html rename to ext/fiddle/fiddle.html index e9edf09c49..3dce67439e 100644 --- a/ext/fiddle/fiddle.in.html +++ b/ext/fiddle/fiddle.html @@ -3,16 +3,16 @@ - sqlite3 fiddle (experimental!) - + sqlite3 fiddle +