NativeScript / ios-jsc

NativeScript for iOS using JavaScriptCore

Home Page:http://docs.nativescript.org/runtimes/ios

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can multiple TNSRuntimes be used from multiple threads?

adrian-niculescu opened this issue · comments

Can the iOS runtime be instantiated multiple times?

UI can only be manipulated from the main thread (the TNSRuntime bounded to the main thread's runloop) - this is mainly a limitation inherited from the iOS and Android platforms. This is ok!

What about code that is not UI-related? One may need to have completely isolated JavaScript code that needs to be run in a separate JS Context.
One way to do this is via the Workers API, which, as I understand, spawns another thread in which another JavaScript virtual machine is created (a new context, global object, etc.). Nevertheless in order for this to work, the worker must a "child" of the main context.

As far as I can see from code such as:
[_tnsRuntime scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

  • There can be multiple TNSRuntime objects
  • a TNSRuntime object is even passed the NSRunLoop object
    So the premises for this to work are there, right?
    Nevertheless, aren't native APIs that should only be run on the main thread used without checking if we are running on the main thread? I am referring to calls to UIApplication object for example.

@adrian-niculescu You're right. The runtime can be instantiated multiple times and can use run loops on worker threads. Calling to APIs which require being called from the main thread from such runtimes, however, will cause runtime errors and should never be done. Meaning that you will not be able to use any of tns-core-modules' UI abstractions.

@mbektchiev Thank you for your response! Just to be clear:
I. You're saying there's nothing in the runtime itself that makes it dependent of the main thread, right?
II. What about native APIs. Let aside expected concurrency issues with them - I am interested in the interaction with NativeScript. Please tell me if I understood this correctly:
If you call a native API from Typescript(JavaScript) running inside a Worker or a TNSRuntime tied to a worker thread's runloop, and the native API is asynchronous the callback that is exposed in JavaScript must to be executed on the same thread where the JS interpreter is running. Is the constraint more relaxed than this?
For example, I make an asynchronous call to an iOS API that goes to another thread and at the end it must call a completionHandler. How would I dispatch the call to the completionHandler (which is reflected in JavaScript) so that it plays nice with NativeScript?
I considered this scheme:

  1. Make a NSOperationQueue with maxConcurrentOperationCount = 1, so a serial queue
  2. Instantiate a TNSRuntime there, run a script and inside there call some async iOS API from JavaScript,
  3. In the iOS code called from JS use NSOperationQueue.currentQueue at the beginning of the async call and remember this information to know to dispatch the completionHandler on the same queue when done.

My worry here - an NSOperationQueue is not tied to 1 Thread, but it can guarantee that the JavaScript code is executed serially.
Is this ok? Is there a better way? How can something like this be done from Workers? Does the NativeScript runtime have code that dispatches native calls on the appropriate thread for the JS interpreter? If so, then nothing special needs to be done by us.

It's even more relaxed. When a native callback to JS is coming from another thread it will be dispatched to the JSC VM that created the JS object that's being called but on the same thread synchronously, no additional dispatch is done. The JSC VM is thread safe and it may need to block for a while -- for example until the main thread completes executing it's current JS script.

This means that if you know that a specific callback you supply to some native API could call you from a worker thread and you need to do UI operations on the main thread, then you have to take care to dispatch those operations accordingly.

@mbektchiev Great! So any async call can end in the completionHandler being natively called on any thread and NativeScript takes care of things when this is then used in JavaScript.
Can you tell me if all this is true for the android runtime, or should I ask a question in that repo also?

We checked what's the case there, and it seems that in Android runtime the behavior is quite different. Any call to JS is dispatched to the thread owning the runtime.

For reference you can see the code of dispatchCallJSMethodNative

@mbektchiev So to conclude for anyone reading this in the future, and please correct me if I am wrong:

  1. The NativeScript runtime can be used from multiple threads - all problems are just the expected ones associated doing this - care when using APIs that are not thread-safe etc.
  2. The NativeScript runtime both on iOS and Android can handle native async calls that end in callbacks being natively called on other worker threads, so as not to break the JavaScript virtual machine.
  1. is not entirely correct: iOS runtime executes the JavaScript of such callbacks synchronously оn the thread it was called, while Android runtime dispatches the JS call to the corresponding runtime instance's threadScheduler

It could be possible to change the behavior in Android but we'll have to research it. If you need calls from worker threads to be executed synchronously, I suggest that you open a new issue in the repository, so that we can plan for it in some of the upcoming releases.

Understood. Thank you very much for your answers and promptitude.
We are looking forward to implementing our mobile applications with the help of NativeScript!