android
android copied to clipboard
Workers API
Support for Workers
For iOS solution please check - https://github.com/NativeScript/ios-runtime/issues/620
Description
General guidelines for the Workers implementation effort and specification in the context of NativeScript. We have an existing issue here, but the purpose here is to show the road map that the team intends to follow in developing the Workers functionality. The general scenarios we want to support are listed at the bottom.
Limitations
In NativeScript we don’t need to implement all details of the web workers specification, because some of these details are only related to the browser, and won’t have any meaning in the context of a NativeScript application. The features and syntax that will follow has the purpose of describing the adoption of the web workers specification in NativeScript. In this document we will describe and list what we intend to support.
Guidelines from the specification to follow
context
Notice that onmessage and postMessage() need to be hung off the Worker object when used in the main script thread, but not when used in the worker. This is because, inside the worker, the worker is effectively the global scope.
messaging specifics
Data passed between the main thread and workers is copied, not shared. Objects are serialized as they're handed to the worker, and subsequently, deserialized on the other end. The page and worker do not share the same instance, so the end result is that a duplicate is created on each end. Most browsers implement this feature as structured cloning. The structured cloning algorithm can accept JSON and a few things that JSON can't — like circular references.
messaging transfer ownership
Passing data by transferring ownership (transferable objects): in nativescript we can try to pass native objects by transferring ownership on them.
onerror spec
The error event has the following three fields that are of interest: message: A human-readable error message. filename: The name of the script file in which the error occurred. lineno: The line number of the script file on which the error occurred.
importScripts
ImportScripts in a nativescript context mean require. If we want to support this syntax we need an alias for require.
Syntax
The following example will be the general syntax to use when working with Workers. Syntax is based on web worker specification.
<app_name>/app/main.js
// create new worker
var myWorker = new Worker("worker.js");
// on worker error handler
myWorker.onerror = function (e) {
//do on worker error
}
// receive messages from worker
myWorker.onmessage = function (e) {
result.textContent = e.data;
}
....
// send messages to worker
myWorker.postMessage("message will be sent to worker");
//The worker thread is killed immediately without an opportunity to complete its operations or clean up after itself.
myWorker.terminate();
myWorker.onclose = function () {
//on close (clean up)
}
<app_name>/app/worker.js
onmessage = function(e) {
postMessage(e.data); // worker -> main
}
//worker closes it self
close();
Supported APIs:
Worker Object:
- [x] Worker.onerror
- [x] Worker.onmessage
- [x] Worker.postMessage()
- [x] Worker.terminate()
Worker Global Object:
- [x] WorkerGlobalObject.self
- [x] WorkerGlobalObject.onmessage
- [x] WorkerGlobalObject.onerror
- [x] WorkerGlobalObject.onclose
- [x] WorkerGlobalObject.close()
- [x] WorkerGlobalObject.postMessage()
- [ ] WorkerGlobalObject.importScripts()
Implementation steps:
- [x] Run pure javascript file on new thread
- [x] Initialize the android runtime on new thread
- [x] Create a run loop on the newly created thread and schedule the runtime on it
- [x] Enable require of pure javascript files from new thread
- [x] Implement error handling per thread
- [x] Try/catch should work like it does in the main thread
- [x] When error is not caught, call
onerror
handler of the worker object on the main thread - [ ] Throw error when calling UI APIs on worker thread (if possible)
- [x] Call native non-ui APIs from javascript file running on thread
- [ ] android runtime tests pass on worker thread
- [x] Implement messaging API to communicate between threads (main -> worker, worker->main)
- [x] Simple object
- [ ] Simple object and an ArrayBuffer as a second parameter
- [ ] Native objects (change of ownership instead of copy)
- [ ] Enable tns-module support (will extend later)
- [ ] Debugging
- [ ] Resarch what should be done on the backend side in order to support worker debugging
- [ ] Debugging support in NativeScriptAppInspector client
- [ ] Debugging support in VSCode
- [x] Add tests
- [x] Callback tests
- [x] Parameters/Properties/Ctor tests
- [x] Lifecycle tests
- [x] GC tests
- [x] Update documentation
Please note; make sure your cohorts on the iOS side have the same "bucket" list... And I really think object ownership transfers is a worth while feature to get in initially so I'm glad to see it on your check list... Circular references, I don't think is that big of a need; there are many JSON libraries that can do this, so this can be easily done from the user side. The basics we need working well. :-)
If you need any help testing features please let me know...
Also consider researching support for shared array buffers and atomics as per the spec https://github.com/tc39/ecmascript_sharedmem
This is already under a flag in Chrome 48+
I have another possible feature "enhancement" -- normally you do:
var myWorker = new Worker("worker.js");
to start a new worker...
Well in the spec you can create a Blob, and then create a BlobURL from that blob and pass it in as the filename, so you can fairly easily dynamically create JS to run inside the worker. I don't know if we want to go to all the trouble to create a Blob api support to support dynamic support; I would be just as happy if we extended the worker so that you could do:
var myWorker = new Worker(null, myWebWorkerCodeString);
to start a new worker, and since the filename is blank; it would use the code I passed in as that string...
Or perhaps
var myWorker = new DynamicWorker(myWebWorkerCodeString);
that new Worker(fileName);
maps to after it loads the JS code... ;-)
So please can we PLEASE (pretty PLEASE) get the webworker interface to accept dynamic strings, not just files... :grinning:
@NathanaelA while experimenting with the worker API, I too wished that I could pass just a string, but could not. In the end it all boils down to a string, that is compiled in v8; only one has a source, and the other - doesn't, so it should be possible. Can't say yet if it will be in on the end of the first iteration of the WebWorkers support.
@NathanaelA How does one go about experimenting with the preview channel of the WebWorkers?
@roblav96 when they are ready, they will be available in the master branch for others to test and experiment with.
You can normally fetch the latest unstable changes from the master
by invoking tns platform add android@next
@Pip3r4o So are you saying I can run tns platform add android@next
to experiment with the WebWorkers
? :D
@roblav96 as soon as they make it into the master branch, yes. We will let you know when that is.
@roblav96 To my knowledge they are not released...
@Pip3r4o Awesome! Thanks so much!
How might one go about applying for dev
branch access? I would really like to become more apart of the community. Hopefully going to nativescript developer day will help with that.
@roblav96 you already have access to 99.9% of what we develop, the Web Workers are still going through internal discussions, architectural planning, and more. We will release a statement very soon with more details on what we have been working on this past month.
We are always glad to welcome new contributors. You can begin by becoming familiar with the execution flow of an android application, and the more "advanced" topics in our runtimes
docs.
There is also an ongoing effort to better document our code, so for now you may be able to understand just enough when going through the specific implementations in our runtimes.
@Pip3r4o Thanks for the heads up. I started my NativeScript endeavour about a month ago and am completely obsessed. I don't consider myself a ninja yet, but I do agree I've come across some things that should be added to the docs. Most importantly how to use android-dts-generator. Am I allowed to to submit a PR to the docs repository? I would like to, but I won't if it will go to waste.
@roblav96 Absolutely, please do! The submodule android-runtime-docs may be more appropriate for the dts-generator. Also, hit me up in our Community Slack (peter.kanev), and I'll assist you with what I can in my spare time!
@Pip3r4o Awesome! Sounds great. I look forward to expanding the documentation. Thank you!
Quick question, would we be able to implement a CursorLoader
with this as suggested on the official android docs?
Workers are slowly crawling their way out, I've updated the checklist above to reflect on what has been implemented so far in the android runtime (multithreading branch). Testing is underway, but unit tests alone will not suffice. Do you have any mini scenarios (please be very specific - plugins, work volume?) that you would like to see implemented? One of the tests performs heavy-duty work on a Worker thread, but we need something not so... generic? @NathanaelA @sitefinitysteve
@Pip3r4o - moving my sqlite plugin to be threaded would be a good test; but I won't be able to even start on this until after the conference... The nativescript-contacts plugin also could use being put into multithreaded code; it has a serious issues when you have a lot of contacts. (On both iOS and Android). So it would also have my vote.
I also have a couple other more specific items that need some threading... So I can run some tests myself after the NS-DevDays conference with them; I'll also get the masters.nativescript.rocks server to start building this branch also for those of us who want to start testing against it...
@NathanaelA The current implementation of the Workers will be available at @next
as soon as we merge the latest v8 into master, which will be any day now, making sure we got everything right, since updating the v8 imposed numerous breaking changes we had to take care of to ensure correct runtime behavior.
@Pip3r4o - Thanks for the heads up on the new version of v8. I'm not sure if you are aware but I've been building masters on the community site for considerably longer than the @next has been around. I also have masters for iOS which is still not offered via @next.. ;-)
@Pip3r4o
I would certainly like to see retrieving contacts through a CursorLoader
implemented and android.
On iOS I think a good test would be to unzip a large archive using ZipArchive
.
@NativeScript/android-runtime we should consider sharing the application Context instance with the other worker threads (passed as a parameter to the WorkerThread constructor inside Java for example) and exposing it. This will enable use of ContentProviders, CursorLoaders and whatnot, that are normally used inside an AsyncTask/new Thread to fetch and operate on data from the device.
CC @roblav96
The tns-core-modules
's utils/utils
module properly returns the application context, either from a public static field in com.tns.NativeScriptApplication
or by resorting to using reflection, so that should be sufficient for the moment for getting the context instance. @roblav96
require('globals') // necessary to bootstrap modules in a new thread
var utils = require("utils/utils");
var ctx = utils.ad.getApplicationContext();
Work on this issue/story will be resumed as soon as we gather enough feedback for the current state of the Workers
In your article (http://developer.telerik.com/featured/benefits-single-threading-model-nativescript/), you mentioned:
For a significant number of these scenarios, NativeScript modules and plugins already exist to do the generic work on a background thread, dispatching the output to the main UI thread when the heavy lifting is done. The out-of-the-box HTTP module, for example, uses a background thread to process network requests
Does the same applies to the fetch module
?
@dxshindeo Yes, the current fetch
module implementation uses xhr
module which in turn uses http
module.
Thanks for clearing that up :)
WEB WORKER IS NOT WORKING IN API17-API19!!! Didn't test API20, but I reccon it also won't work. Web Worker successfully works in API21-API25. This is a major dealbreaker!
@dxshindeo it would be of great help if you shared what happens on devices of lower API level and what isn't working for you. Thank you!
Basically, main file:
var sockets_worker = new Worker('../../workers/sockets');
console.log("A");
sockets_worker.postMessage({
doesnt_matter_test_var: "testA"
});
sockets_worker.onmessage = function(msg) {
console.log("C");
}
And worker file:
require('globals'); // necessary to bootstrap tns modules on the new thread
onmessage = function(msg) {
console.log("B");
postMessage({
doesnt_matter_test_var: "testB"
});
}
This works fine in API21+, but in lesser APIs I get only console.log("A");
, no other console.log is given out.
@dxshindeo I just tested the above, and it works out well for me, messages are logged properly and communication works back and forth. Tested on Geny Emulator API 17 and native AVD API18 Emulator. Please provide more details like crash logs, or steps to reproduce the behavior you are experiencing. Thanks!