In Emscripten C++ / wbasm how does one get an "on page closed" event - html

I have C+ program that compiles to web assebmbly using the emscripten system. I would like to clean up some things, flush files, etc etc. when he page running the program is closed.
in main there is:
emscripten_set_main_loop_arg(onMainLoopTick, arg, 0, 1);
Currently when the page closes the "process" is simply exited and does not continue after the "loop simulator". I figure I need to get an event from the page that will block the main thread until the C++ code process it and cleans up it's mess.
What event should I forward to C++ and how should I use it ?

The first things to know is that there is no native library nor APIs for WebAssembly (I mean..yet, as of MVP. There are native features like threads coming as post-MVP feature). What is means that all system libraries in C++ are implemented by importing emulated JavaScript functions. So if you are looking for native features like detecting closing events, you should check if there is JS/HTML5 APIs that do the similar things.
To see how it works, open generated .wast file and search for import instructions and generated JS files. Also, you may want to search on Emscripten repo directly to check if there is JS/HTML5 bindings available on C++ side, as their documentation is quite large and hard to look through.
Sticking to the point, the HTML5 events that are fired when closing are beforeunload and unload. I would prefer using beforeunload event. Emscripten provides em_beforeunload_callback callback function type and emscripten_set_beforeunload_callback to register in html5.h bindings.
Otherwise, you use them directly. For example:
In C++:
void EMSCRIPTEN_KEEPALIVE clean_stuff() {
// Clean up the mess...
// You should use EMSCRIPTEN_KEEPALIVE or
// add it to EXPORTED_FUNCTIONS in emcc compilation options
// to make it callable in JS side.
}
In JS:
window.addEventListener("beforeunload", function (event) {
// Exported functions are prefixed by an underscore
Module._clean_stuff();
});

Related

SolidJS: "computations created outside a `createRoot` or `render` will never be disposed" messages in the console log

When working on a SolidJS project you might start seeing the following warning message in your JS console:
computations created outside a `createRoot` or `render` will never be disposed
There are some information available on this in SolidJS' Github repository issues. But after reading them I was still not quite sure what this was all about and whether my code was really doing something wrong.
I managed to track down where it came from and find a fix for it based on the documentation. So I'm providing the explanation and the solution for those Googling this warning message.
In essence this is a warning about a possibility of a memory leak due to a reactive computation being created without the proper context which would dispose of it when no longer needed.
A proper context is created a couple of different ways. Here are the ones I know about:
By using the render function.
By using the createRoot function. Under the hood render uses this.
By using the createContext function.
The first is by far the most common way, because each app has at least one render function call to get the whole show started.
So what makes the code go "out of context"?
Probably the most common way is via async calls. The context creation with its dependency tree happens only when the synchronous portion of the code finishes running. This includes all the export default function in your modules and the main app function.
But code that runs at a later time because of a setTimeout or by being in an async function will be outside of this context and any reactive computations created will not be tracked and might stick around without being garbage collected.
An example
Let's say you have a data input screen and have a Save button on it that makes an API call to your server to save the data. And you want to provide a feedback to the user whether the operation succeeded or not, with a nice HTML formatted message.
[msg,setMsg] = createSignal(<></>)
async function saveForm(){
...
setMsg(<p>Saving your data.<i>Please stand by...</i></p>)
const result=await callApi('updateUser',formData)
if(result.ok){
setMsg(<p>Your changes were <b>successfully</b> saved!</p> )
} else {
setMsg(<p>There was a problem saving your data! <br>Error: </p><pre>{result.error}</pre> )
}
}
...
<div>
...
<button onClick={saveForm} >Save</button>
{msg()}
</div>
This will produce the above mentioned warning when the API call returns an error, but not the other times. Why?
The reason for this is that SolidJS considers the code inserts inside JSX to be reactive, ie: need to be watched and re-evaluated. So inserting the error message from the API call creates a reactive computation.
The solution
I found the solution at the very end of the SolidJS doc. It's a special JSX modifier: /*#once*/
It can be used at the beginning of a curly brace expression and it tells the SolidJS compiler to explicitly not to make this a reactive expression. In other words: it will evaluated once and only once when the DOM nodes are created from the JSX.
In the above example here's how to use it:
setMsg(<p>There was a problem saving your data! <br>Error: </p><pre>{/*#once*/ result.error}</pre> )
After this there will be no more warning messages :)
In my case, I had an input and when that input changed I re-created an SVG drawing. Because the SVG creation was an expensive operation, I added a debounce in the createEffect function which ran when the input changed. debounce is a technique to defer the processing until the input stops changing for at least X amount of time. It involved running the SVG generation code inside the setTimeout function, thus being outside of the main context. Using the /*#once*/ modifier everywhere where I inserted an expression in the generated JSX has fixed the problem.

Debugging experimental WebAssembly externref bug in Google Chrome

Warning: as the reference types proposal isn't complete yet, this code will not run without toggling flags or setting in order to enable executing experimental code.
If you are on Google Chrome or a Chromium browser, you will need to enable the following flag:
chrome://flags/#enable-experimental-webassembly-features
I had set up a simple handwritten Wasm module for personal use. I could've easily written it in JavaScript, but it was easier and made more sense to use Wasm, and since it was a simple, personal script, I wouldn't care if other people couldn't run it.
I had compiled it using wabt's wat2wasm.
The Wasm module was intended to be fed the entire globalThis object to import from.
From there, it took four TypedArray constructors: Uint8Array, Uint16Array, Uint32Array, and BigUint64Array.
Take note: no code was executed prior to the Wasm, thus there cannot be any interference.
Later, I had realized that that the Wasm wasn't working as intended at all, my math was correct, but the variables were wrong.
I had narrowed my problem down to just this:
;; global -> global variable
(import "globalThis" "Uint8Array" (global $Uint8Array externref))
(import "globalThis" "Uint16Array" (global $Uint16Array externref))
(import "globalThis" "Uint32Array" (global $Uint32Array externref))
(import "globalThis" "BigUint64Array" (global $BigUint64Array externref))
;; func -> function
(import "console" "log" (func $console::log (param externref)))
(start $_start)
(func $_start
global.get $Uint8Array
call $console::log
global.get $Uint16Array
call $console::log
global.get $Uint32Array
call $console::log
global.get $BigUint64Array
call $console::log
)
This Wasm was instantiated like so:
WebAssembly.instantiateStreaming(
fetch(
"test.wasm", {
mode : "same-origin",
credentials : "omit",
cache : "no-store",
redirect : "error",
referrer : "no-referrer"
}
), globalThis
).catch( console.error );
The interesting problem is that the logs all say the same thing: Uint8Array.
I was dumbfounded. This has to be literally impossible. The JavaScript file itself was not cached, the WebAssembly was being fetched with "no-cache," the web page itself wasn't cached.
Then I thought, because I was using XHTML, maybe it didn't happen in HTML files. It did there too.
I tried converting the Wasm file to a TypedArray and just using WebAssembly.instantiate, suddenly, it worked flawlessly.
At this point the server must be serving the wrong file, because the further I go, the more this seems like nonsense.
I almost want to say that this is a Chromium browser or V8 runtime error, but I need to narrow it down a bit more before I blindly attempt to present this as a bug.
I have set up two different versions of the same code, on Repl.it, and on CodeSandBox.io, so that hopefully someone can try running it themself, to confirm the bug, and maybe attempt to debug where I went wrong.
(Could this be a problem with Repl.it's server?)
This was a compiler bug fixed by Chromium in
https://chromium-review.googlesource.com/c/v8/v8/+/2551100.
This was the response that I had gotten from one of the developers:
This is indeed a timing issue that has been fixed in https://chromium-review.googlesource.com/c/v8/v8/+/2551100. The problem happens when there are only imported globals, and compilation of the WebAssembly functions finishes before the stream actually finishes. In this case, the offset calculation happens after the compiler uses the offsets, and therefore produces incorrect code.
A workaround is to define one global that is not imported, as this causes the offset calculation to happen earlier.
Seems like sending a small module that only imports globals instead of functions was breaking the code.
Their code had a threading race condition between the stream and the compiler.

Can an embedded cocos2d-js app call back out to c++?

I'm researching the possibility of using cocos2d-js by embedding it as a view inside an existing iOS app. In order to make this work, I'm going to need 2-way communication between cocos2d and the surrounding application.
After some initial investigation, I have determined that it is possible to call in to cocos using ScriptingCore:
ScriptingCore* sc = ScriptingCore::getInstance();
jsval outVal;
sc->evalString("function()", &outVal);
My question, then, is around doing the reverse. It is possible to (e.g. in response to user input) call back out of cocos2d-js to C++? Ideally, there would be a way to register a callback with ScriptingCore which could be invoked from JavaScript.
I believe it can be done, but I have not tried myself, nor can I find a good and concise example.
All I can do is point you at SuperSuraccoon's Bluetooth example and it's git page, which apparently does both ways communication between C++ and JS code.

Why does importing javafx.scene.control.Button fail with Toolkit Not Initialized?

I'm trying basic stuff with JavaFX with Clojure, on Java8 64-bit (1.8.0_05-b13) on Windows 7.
In my imports (whether in .clj file or in REPL), I can (import 'javafx.scene.control.ButtonBuilder) (and other builders), but I cannot (import 'javafx.scene.control.Button) or any other final widget from javax.scene.control.
If I try importing javafx.scene.control.Button or other widget, I get the Toolkit Not Initialized error. Same with trying to create the button via the ButtonBuilder, even though the ButtonBuilder class itself seems to work fine, and it appears I'm quite able to import other things from the javafx heirarchy.
In order to get it to work I have to force the toolkit to initialize, as shown here, which I think leaves me with an Orphan panel somewhere, which feels kind of dirty: https://gist.github.com/zilti/6286307
(ns hello.core
(:import (javafx.event ActionEvent EventHandler)
(javafx.scene Scene SceneBuilder)
(javafx.scene.layout VBox VBoxBuilder)
;;(javafx.scene.control Button) -- MUST COMMENT THIS OUT OR FAIL
(javafx.scene.control ButtonBuilder)
(javafx.stage Stage StageBuilder)))
(defonce force-toolkit-init (javafx.embed.swing.JFXPanel.))
This was not the case with Java 7 and javafxrt.jar. The only discussion about this I've found (on SO) shows this is required for Swing interop, which I'm not using.
Can someone please explain why this is required now with Java8, and why it only seems to be required for final widgets like Button?
This looks like a magic workaround. Is there a real solution somehow?
JavaFX requires initialization code which starts UI threads, handles application running modes and load native libraries.
JavaFX application are strongly advised to start from a class extending javafx.appication.Application which will perform all initialization routines.
Calling JFXPanel will perform initialization too but it's kinda hack (unless you are really using swing and FX in one app).

How to solve "Native methods are not allowed in loaded code" error

I want to let my app to run sound while the playbook in standby mode, I put this statement in the start up
QNXSystem.system.inactivePowerMode = QNXSystemPowerMode.THROTTLED;
Now when I debug the app on the simulator (not desktop debugger) I got this error
VerifyError: Error #1079: Native methods are not allowed in loaded code.
And this error I got also when using AlertDialog.
Note: I am using Flash builder, and I have put the qnx SWC in the libraries path.
.... so to solve these problems?
To allow code compiled w/native extensions to run on the simulator, we had to put code that used native extensions in methods that would never get executed (when on the simulator).
It wasn't enough to just wrap the offending code in an if/else block. The if/else needs to call another method that either has the native version or the simulator version of the code.
For example:
private function showNativeOrFlexAlert(message:String):void
{
// we used the Capabilities class to determine this, might be a better way
if (isMobile)
showNativeAlert(message);
else
showFlexAlert(message);
}
// have to be careful here, this method signature CANNOT include
// any classes from native extension -- no errors on device, but fails on simulator
private function showNativeAlert(message:String):void
{
// use native API to show alert
}
private function showFlexAlert(message:String):void
{
// use the Flex Alert class
}
Set the qnx-air.swc linkage to "external".