Skip to content

Commit fb3997b

Browse files
committed
feat: multithreaded javascript (part II):
- JNI calls are now thread safe - Ability to retrieve the runtime from the currently entered isolate
1 parent 89a10bd commit fb3997b

File tree

11 files changed

+136
-78
lines changed

11 files changed

+136
-78
lines changed

test-app/app/src/main/assets/app/mainpage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ shared.runWeakRefTests();
2121
shared.runRuntimeTests();
2222
shared.runWorkerTests();
2323
require("./tests/testWebAssembly");
24+
require("./tests/testMultithreadedJavascript");
2425
require("./tests/testInterfaceDefaultMethods");
2526
require("./tests/testInterfaceStaticMethods");
2627
require("./tests/testMetadata");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
describe("Test multithreaded javascript ", () => {
2+
it("Should execute callbacks on specified native thread", done => {
3+
const currentThreadId = java.lang.Thread.currentThread().getId();
4+
new java.lang.Thread(new java.lang.Runnable({
5+
run() {
6+
const threadId = java.lang.Thread.currentThread().getId();
7+
expect(threadId).not.toEqual(currentThreadId);
8+
9+
const mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
10+
// done() must called on the main thread to ensure that jasmine tests
11+
// continue executing there
12+
mainHandler.post(new java.lang.Runnable({
13+
run: done
14+
}));
15+
}
16+
})).start();
17+
});
18+
});

test-app/app/src/main/java/com/tns/RuntimeHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public static Runtime initRuntime(Context context) {
7474

7575
Logger logger = new LogcatLogger(context);
7676

77+
Runtime.nativeLibraryLoaded = true;
7778
Runtime runtime = null;
7879
boolean showErrorIntent = hasErrorIntent(context);
7980
if (!showErrorIntent) {

test-app/runtime/src/main/cpp/JsArgConverter.cpp

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ using namespace std;
1515
using namespace tns;
1616

1717
JsArgConverter::JsArgConverter(const Local<Object>& caller, const v8::FunctionCallbackInfo<Value>& args, const string& methodSignature, MetadataEntry* entry)
18-
: m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()) {
18+
: m_isolate(args.GetIsolate()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()) {
1919
int v8ProvidedArgumentsLength = args.Length();
2020
m_argsLen = 1 + v8ProvidedArgumentsLength;
2121

@@ -48,7 +48,7 @@ JsArgConverter::JsArgConverter(const Local<Object>& caller, const v8::FunctionCa
4848
}
4949

5050
JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo<Value>& args, bool hasImplementationObject, const string& methodSignature, MetadataEntry* entry)
51-
: m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()) {
51+
: m_isolate(args.GetIsolate()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()) {
5252
m_argsLen = !hasImplementationObject ? args.Length() : args.Length() - 1;
5353

5454
if (m_argsLen > 0) {
@@ -74,7 +74,7 @@ JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo<Value>& args, bool
7474
}
7575

7676
JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo<Value>& args, const string& methodSignature)
77-
: m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()) {
77+
: m_isolate(args.GetIsolate()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()) {
7878
m_argsLen = args.Length();
7979

8080
JniSignatureParser parser(m_methodSignature);
@@ -366,76 +366,77 @@ bool JsArgConverter::ConvertJavaScriptArray(const Local<Array>& jsArr, int index
366366
jclass elementClass;
367367
string strippedClassName;
368368

369+
JEnv env;
369370
switch (elementTypePrefix) {
370371
case 'Z':
371-
arr = m_env.NewBooleanArray(arrLength);
372+
arr = env.NewBooleanArray(arrLength);
372373
for (jsize i = 0; i < arrLength; i++) {
373374
jboolean value = jsArr->Get(context, i).ToLocalChecked()->BooleanValue(m_isolate);
374-
m_env.SetBooleanArrayRegion((jbooleanArray) arr, i, 1, &value);
375+
env.SetBooleanArrayRegion((jbooleanArray) arr, i, 1, &value);
375376
}
376377
break;
377378
case 'B':
378-
arr = m_env.NewByteArray(arrLength);
379+
arr = env.NewByteArray(arrLength);
379380
for (jsize i = 0; i < arrLength; i++) {
380381
jbyte value = jsArr->Get(context, i).ToLocalChecked()->Int32Value(context).ToChecked();
381-
m_env.SetByteArrayRegion((jbyteArray) arr, i, 1, &value);
382+
env.SetByteArrayRegion((jbyteArray) arr, i, 1, &value);
382383
}
383384
break;
384385
case 'C':
385-
arr = m_env.NewCharArray(arrLength);
386+
arr = env.NewCharArray(arrLength);
386387
for (jsize i = 0; i < arrLength; i++) {
387388
String::Utf8Value utf8(m_isolate, jsArr->Get(context, i).ToLocalChecked()->ToString(context).ToLocalChecked());
388-
JniLocalRef s(m_env.NewString((jchar*) *utf8, 1));
389-
const char* singleChar = m_env.GetStringUTFChars(s, nullptr);
389+
JniLocalRef s(env.NewString((jchar*) *utf8, 1));
390+
const char* singleChar = env.GetStringUTFChars(s, nullptr);
390391
jchar value = *singleChar;
391-
m_env.ReleaseStringUTFChars(s, singleChar);
392-
m_env.SetCharArrayRegion((jcharArray) arr, i, 1, &value);
392+
env.ReleaseStringUTFChars(s, singleChar);
393+
env.SetCharArrayRegion((jcharArray) arr, i, 1, &value);
393394
}
394395
break;
395396
case 'S':
396-
arr = m_env.NewShortArray(arrLength);
397+
arr = env.NewShortArray(arrLength);
397398
for (jsize i = 0; i < arrLength; i++) {
398399
jshort value = jsArr->Get(context, i).ToLocalChecked()->Int32Value(context).ToChecked();
399-
m_env.SetShortArrayRegion((jshortArray) arr, i, 1, &value);
400+
env.SetShortArrayRegion((jshortArray) arr, i, 1, &value);
400401
}
401402
break;
402403
case 'I':
403-
arr = m_env.NewIntArray(arrLength);
404+
arr = env.NewIntArray(arrLength);
404405
for (jsize i = 0; i < arrLength; i++) {
405406
jint value = jsArr->Get(context, i).ToLocalChecked()->Int32Value(context).ToChecked();
406-
m_env.SetIntArrayRegion((jintArray) arr, i, 1, &value);
407+
env.SetIntArrayRegion((jintArray) arr, i, 1, &value);
407408
}
408409
break;
409410
case 'J':
410-
arr = m_env.NewLongArray(arrLength);
411+
arr = env.NewLongArray(arrLength);
411412
for (jsize i = 0; i < arrLength; i++) {
412413
jlong value = jsArr->Get(context, i).ToLocalChecked()->NumberValue(context).ToChecked();
413-
m_env.SetLongArrayRegion((jlongArray) arr, i, 1, &value);
414+
env.SetLongArrayRegion((jlongArray) arr, i, 1, &value);
414415
}
415416
break;
416417
case 'F':
417-
arr = m_env.NewFloatArray(arrLength);
418+
arr = env.NewFloatArray(arrLength);
418419
for (jsize i = 0; i < arrLength; i++) {
419420
jfloat value = jsArr->Get(context, i).ToLocalChecked()->NumberValue(context).ToChecked();
420-
m_env.SetFloatArrayRegion((jfloatArray) arr, i, 1, &value);
421+
env.SetFloatArrayRegion((jfloatArray) arr, i, 1, &value);
421422
}
422423
break;
423424
case 'D':
424-
arr = m_env.NewDoubleArray(arrLength);
425+
arr = env.NewDoubleArray(arrLength);
425426
for (jsize i = 0; i < arrLength; i++) {
426427
jdouble value = jsArr->Get(context, i).ToLocalChecked()->NumberValue(context).ToChecked();
427-
m_env.SetDoubleArrayRegion((jdoubleArray) arr, i, 1, &value);
428+
env.SetDoubleArrayRegion((jdoubleArray) arr, i, 1, &value);
428429
}
429430
break;
430431
case 'L':
431432
strippedClassName = elementType.substr(1, elementType.length() - 2);
432-
elementClass = m_env.FindClass(strippedClassName);
433-
arr = m_env.NewObjectArray(arrLength, elementClass, nullptr);
433+
elementClass = env.FindClass(strippedClassName);
434+
arr = env.NewObjectArray(arrLength, elementClass, nullptr);
434435
for (int i = 0; i < arrLength; i++) {
435436
auto v = jsArr->Get(context, i).ToLocalChecked();
436437
JsArgToArrayConverter c(context, v, false, (int) Type::Null);
437438
jobject o = c.GetConvertedArg();
438-
m_env.SetObjectArrayElement((jobjectArray) arr, i, o);
439+
env.SetObjectArrayElement((jobjectArray) arr, i, o);
439440
}
440441
break;
441442
default:
@@ -515,10 +516,11 @@ JsArgConverter::Error JsArgConverter::GetError() const {
515516

516517
JsArgConverter::~JsArgConverter() {
517518
if (m_argsLen > 0) {
519+
JEnv env;
518520
int length = m_storedObjects.size();
519521
for (int i = 0; i < length; i++) {
520522
int index = m_storedObjects[i];
521-
m_env.DeleteLocalRef(m_args[index].l);
523+
env.DeleteLocalRef(m_args[index].l);
522524
}
523525
}
524526
}

test-app/runtime/src/main/cpp/JsArgConverter.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ class JsArgConverter {
5555
template<typename T>
5656
bool ConvertFromCastFunctionObject(T value, int index);
5757

58-
JEnv m_env;
59-
6058
v8::Isolate* m_isolate;
6159

6260
int m_argsLen;

0 commit comments

Comments
 (0)