Skip to content

Commit bdd0313

Browse files
authored
feat: runOnMain, postFrameCallback & removeFrameCallback (#1713)
* feat: runOnMain, postFrameCallback & removeFrameCallback * fix: frame cache getter * fix: add isMainThread bool & clean up fd on close * chore: clean up * fix: clean fd & looper
1 parent cd5cc1d commit bdd0313

File tree

12 files changed

+3290
-339
lines changed

12 files changed

+3290
-339
lines changed

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

+24-21
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,36 @@ var MyActivity = (function (_super) {
2929
MyActivity.prototype.onCreate = function (bundle) {
3030
_super.prototype.onCreate.call(this, bundle);
3131

32-
require("./tests/testsWithContext").run(this);
33-
execute(); //run jasmine
32+
require('./tests/testsWithContext').run(this);
33+
execute(); //run jasmine
3434

35-
var layout = new android.widget.LinearLayout(this);
36-
layout.setOrientation(1);
37-
this.setContentView(layout);
35+
var layout = new android.widget.LinearLayout(this);
36+
layout.setOrientation(1);
37+
this.setContentView(layout);
3838

39-
var textView = new android.widget.TextView(this);
40-
textView.setText("It's a button!");
41-
layout.addView(textView);
39+
var textView = new android.widget.TextView(this);
40+
textView.setText("It's a button!");
41+
layout.addView(textView);
4242

43-
var button = new android.widget.Button(this);
44-
button.setText("Hit me");
45-
layout.addView(button);
46-
var counter = 0;
43+
var button = new android.widget.Button(this);
44+
button.setText('Hit me');
45+
layout.addView(button);
46+
var counter = 0;
4747

48-
var Color = android.graphics.Color;
49-
var colors = [Color.BLUE, Color.RED, Color.MAGENTA, Color.YELLOW, Color.parseColor("#FF7F50")];
50-
var taps = 0;
48+
var Color = android.graphics.Color;
49+
var colors = [Color.BLUE, Color.RED, Color.MAGENTA, Color.YELLOW, Color.parseColor('#FF7F50')];
50+
var taps = 0;
5151

52-
var dum = com.tns.tests.DummyClass.null;
52+
var dum = com.tns.tests.DummyClass.null;
5353

54-
button.setOnClickListener(new android.view.View.OnClickListener("AppClickListener", {
55-
onClick: function() {
56-
button.setBackgroundColor(colors[taps % colors.length]);
57-
taps++;
58-
}}));
54+
button.setOnClickListener(
55+
new android.view.View.OnClickListener('AppClickListener', {
56+
onClick: function () {
57+
button.setBackgroundColor(colors[taps % colors.length]);
58+
taps++;
59+
},
60+
})
61+
);
5962

6063
};
6164
MyActivity = __decorate([

test-app/app/src/main/assets/app/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"maxLogcatObjectSize": 1024,
1111
"forceLog": false,
1212
"suppressCallJSMethodExceptions": false,
13-
"enableLineBreakpoints": false
13+
"enableLineBreakpoints": false,
14+
"enableMultithreadedJavascript": true
1415
},
1516
"discardUncaughtJsExceptions": false
1617
}

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

+546-298
Large diffs are not rendered by default.

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

+114
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "ArrayElementAccessor.h"
1515
#include "ObjectManager.h"
1616
#include "include/v8.h"
17+
#include "robin_hood.h"
1718

1819
namespace tns {
1920
class CallbackHandlers {
@@ -73,6 +74,11 @@ namespace tns {
7374
static void SetJavaField(v8::Isolate *isolate, const v8::Local<v8::Object> &target,
7475
const v8::Local<v8::Value> &value, FieldCallbackData *fieldData);
7576

77+
78+
static void RunOnMainThreadCallback(const v8::FunctionCallbackInfo<v8::Value> &args);
79+
80+
static int RunOnMainThreadFdCallback(int fd, int events, void* data);
81+
7682
static void LogMethodCallback(const v8::FunctionCallbackInfo<v8::Value> &args);
7783

7884
static void TimeCallback(const v8::FunctionCallbackInfo<v8::Value> &args);
@@ -187,6 +193,39 @@ namespace tns {
187193
jstring stackTrace, jstring filename, jint lineno,
188194
jstring threadName);
189195

196+
static void RemoveIsolateEntries(v8::Isolate *isolate);
197+
198+
199+
static void PostFrameCallback(const v8::FunctionCallbackInfo<v8::Value> &args);
200+
201+
static void RemoveFrameCallback(const v8::FunctionCallbackInfo<v8::Value> &args);
202+
203+
struct AChoreographer;
204+
205+
206+
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
207+
208+
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
209+
210+
typedef AChoreographer* (*func_AChoreographer_getInstance)();
211+
212+
typedef void (*func_AChoreographer_postFrameCallback)(
213+
AChoreographer* choreographer, AChoreographer_frameCallback callback,
214+
void* data);
215+
216+
typedef void (*func_AChoreographer_postFrameCallback64)(
217+
AChoreographer* choreographer, AChoreographer_frameCallback64 callback,
218+
void* data);
219+
220+
typedef void (*func_AChoreographer_postFrameCallbackDelayed)(
221+
AChoreographer* choreographer, AChoreographer_frameCallback callback,
222+
void* data, long delayMillis);
223+
224+
typedef void (*func_AChoreographer_postFrameCallbackDelayed64)(
225+
AChoreographer* choreographer, AChoreographer_frameCallback64 callback,
226+
void* data, uint32_t delayMillis);
227+
228+
190229
private:
191230
CallbackHandlers() {
192231
}
@@ -242,7 +281,82 @@ namespace tns {
242281
jobject _runtime;
243282
};
244283

284+
static void RemoveKey(const uint32_t key);
285+
286+
static _Atomic uint32_t count_;
287+
288+
struct CacheEntry {
289+
CacheEntry(v8::Isolate* isolate, v8::Persistent<v8::Function>* callback)
290+
: isolate_(isolate),
291+
callback_(callback) {
292+
}
293+
294+
v8::Isolate* isolate_;
295+
v8::Persistent<v8::Function>* callback_;
296+
297+
};
298+
299+
static robin_hood::unordered_map<uint32_t, CacheEntry> cache_;
300+
301+
302+
static _Atomic uint64_t frameCallbackCount_;
303+
304+
struct FrameCallbackCacheEntry {
305+
FrameCallbackCacheEntry(v8::Isolate *isolate, v8::Persistent<v8::Function> *callback)
306+
: isolate_(isolate),
307+
callback_(callback) {
308+
}
309+
310+
v8::Isolate *isolate_;
311+
v8::Persistent<v8::Function> *callback_;
312+
bool removed;
313+
uint64_t id;
314+
315+
AChoreographer_frameCallback frameCallback_ = [](long ts, void *data) {
316+
execute((double)ts, data);
317+
};
318+
319+
AChoreographer_frameCallback64 frameCallback64_ = [](int64_t ts, void *data) {
320+
execute((double)ts, data);
321+
};
322+
323+
static void execute(double ts, void *data){
324+
if (data != nullptr) {
325+
auto entry = static_cast<FrameCallbackCacheEntry *>(data);
326+
if (entry->removed) {
327+
return;
328+
}
329+
v8::Isolate *isolate = entry->isolate_;
330+
331+
v8::Persistent<v8::Function> *poCallback = entry->callback_;
332+
333+
v8::Locker locker(isolate);
334+
v8::Isolate::Scope isolate_scope(isolate);
335+
v8::HandleScope handle_scope(isolate);
336+
auto context = v8::Context::New(isolate);
337+
v8::Context::Scope context_scope(context);
338+
339+
340+
v8::Local<v8::Function> cb = poCallback->Get(isolate);
341+
v8::Local<v8::Value> result;
342+
343+
v8::Local<v8::Value> args[1] = {v8::Number::New(isolate, ts)};
344+
345+
if (!cb->Call(context, context->Global(), 1, args).ToLocal(&result)) {
346+
assert(false);
347+
}
348+
349+
}
350+
}
351+
352+
};
353+
354+
static robin_hood::unordered_map<uint64_t, FrameCallbackCacheEntry> frameCallbackCache_;
355+
356+
static void InitChoreographer();
245357

358+
static void PostCallback(const v8::FunctionCallbackInfo<v8::Value> &args,
359+
FrameCallbackCacheEntry *entry, v8::Local<v8::Context> context);
246360
};
247361
}
248362

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

+34-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "ManualInstrumentation.h"
3131
#include <snapshot_blob.h>
3232
#include "IsolateDisposer.h"
33+
#include <unistd.h>
3334

3435
#ifdef APPLICATION_IN_DEBUG
3536
#include "JsV8InspectorClient.h"
@@ -209,6 +210,21 @@ Runtime::~Runtime() {
209210
delete this->m_loopTimer;
210211
delete this->m_heapSnapshotBlob;
211212
delete this->m_startupData;
213+
CallbackHandlers::RemoveIsolateEntries(m_isolate);
214+
if (m_isMainThread) {
215+
if (m_mainLooper_fd[0] != -1) {
216+
ALooper_removeFd(m_mainLooper, m_mainLooper_fd[0]);
217+
}
218+
ALooper_release(m_mainLooper);
219+
220+
if (m_mainLooper_fd[0] != -1) {
221+
close(m_mainLooper_fd[0]);
222+
}
223+
224+
if (m_mainLooper_fd[1] != -1) {
225+
close(m_mainLooper_fd[1]);
226+
}
227+
}
212228
}
213229

214230
std::string Runtime::ReadFileText(const std::string& filePath) {
@@ -625,13 +641,21 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native
625641
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__time"), FunctionTemplate::New(isolate, CallbackHandlers::TimeCallback));
626642
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__releaseNativeCounterpart"), FunctionTemplate::New(isolate, CallbackHandlers::ReleaseNativeCounterpartCallback));
627643
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__markingMode"), Number::New(isolate, m_objectManager->GetMarkingMode()), readOnlyFlags);
628-
629-
644+
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__runOnMainThread"), FunctionTemplate::New(isolate, CallbackHandlers::RunOnMainThreadCallback));
645+
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__postFrameCallback"), FunctionTemplate::New(isolate, CallbackHandlers::PostFrameCallback));
646+
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__removeFrameCallback"), FunctionTemplate::New(isolate, CallbackHandlers::RemoveFrameCallback));
630647
/*
631648
* Attach `Worker` object constructor only to the main thread (isolate)'s global object
632649
* Workers should not be created from within other Workers, for now
633650
*/
634651
if (!s_mainThreadInitialized) {
652+
m_isMainThread = true;
653+
pipe(m_mainLooper_fd);
654+
m_mainLooper = ALooper_forThread();
655+
ALooper_acquire(m_mainLooper);
656+
657+
ALooper_addFd(m_mainLooper, m_mainLooper_fd[0], 0, ALOOPER_EVENT_INPUT, CallbackHandlers::RunOnMainThreadFdCallback, nullptr);
658+
635659
Local<FunctionTemplate> workerFuncTemplate = FunctionTemplate::New(isolate, CallbackHandlers::NewThreadCallback);
636660
Local<ObjectTemplate> prototype = workerFuncTemplate->PrototypeTemplate();
637661

@@ -651,6 +675,7 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native
651675
* Attach 'postMessage', 'close' to the global object
652676
*/
653677
else {
678+
m_isMainThread = false;
654679
auto postMessageFuncTemplate = FunctionTemplate::New(isolate, CallbackHandlers::WorkerGlobalPostMessageCallback);
655680
globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "postMessage"), postMessageFuncTemplate);
656681
auto closeFuncTemplate = FunctionTemplate::New(isolate, CallbackHandlers::WorkerGlobalCloseCallback);
@@ -822,10 +847,17 @@ int Runtime::GetId() {
822847
return this->m_id;
823848
}
824849

850+
int Runtime::GetWriter(){
851+
return m_mainLooper_fd[1];
852+
}
853+
825854
JavaVM* Runtime::s_jvm = nullptr;
826855
jmethodID Runtime::GET_USED_MEMORY_METHOD_ID = nullptr;
827856
map<int, Runtime*> Runtime::s_id2RuntimeCache;
828857
map<Isolate*, Runtime*> Runtime::s_isolate2RuntimesCache;
829858
bool Runtime::s_mainThreadInitialized = false;
830859
v8::Platform* Runtime::platform = nullptr;
831860
int Runtime::m_androidVersion = Runtime::GetAndroidVersion();
861+
ALooper* Runtime::m_mainLooper = nullptr;
862+
int Runtime::m_mainLooper_fd[2];
863+

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

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "MessageLoopTimer.h"
1313
#include "File.h"
1414
#include <mutex>
15+
#include <android/looper.h>
1516

1617
namespace tns {
1718
class Runtime {
@@ -69,6 +70,8 @@ class Runtime {
6970

7071
std::string ReadFileText(const std::string& filePath);
7172

73+
static int GetWriter();
74+
7275
private:
7376
Runtime(JNIEnv* env, jobject runtime, int id);
7477

@@ -98,6 +101,8 @@ class Runtime {
98101

99102
v8::Persistent<v8::Context>* m_context;
100103

104+
bool m_isMainThread;
105+
101106
v8::Isolate* PrepareV8Runtime(const std::string& filesPath, const std::string& nativeLibsDir, const std::string& packageName, bool isDebuggable, const std::string& callingDir, const std::string& profilerOutputDir, const int maxLogcatObjectSize, const bool forceLog);
102107
jobject ConvertJsValueToJavaObject(JEnv& env, const v8::Local<v8::Value>& value, int classReturnType);
103108
static v8::StartupData CreateSnapshotDataBlob(const char* embedded_source);
@@ -115,6 +120,10 @@ class Runtime {
115120

116121
static bool s_mainThreadInitialized;
117122

123+
static ALooper* m_mainLooper;
124+
125+
static int m_mainLooper_fd[2];
126+
118127
#ifdef APPLICATION_IN_DEBUG
119128
std::mutex m_fileWriteMutex;
120129
#endif

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

+10-10
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ string Util::JniClassPathToCanonicalName(const string& jniClassPath) {
2424

2525
case '[':
2626
canonicalName = jniClassPath;
27-
lastIndex = canonicalName.find_last_of("[");
27+
lastIndex = canonicalName.find_last_of('[');
2828
rest = canonicalName.substr(lastIndex + 1);
2929
canonicalName = canonicalName.substr(0, lastIndex + 1);
3030
canonicalName.append(JniClassPathToCanonicalName(rest));
@@ -54,17 +54,17 @@ void Util::SplitString(const string& str, const string& delimiters, vector<strin
5454
if (tokenPos < delimPos) {
5555
tokens.push_back(str.substr(pos, delimPos - pos));
5656
} else {
57-
tokens.push_back("");
57+
tokens.emplace_back("");
5858
}
5959
} else {
60-
tokens.push_back("");
60+
tokens.emplace_back("");
6161
}
6262
pos = delimPos + 1;
6363
} else {
6464
if (string::npos != tokenPos) {
6565
tokens.push_back(str.substr(pos));
6666
} else {
67-
tokens.push_back("");
67+
tokens.emplace_back("");
6868
}
6969
break;
7070
}
@@ -112,19 +112,19 @@ u16string Util::ConvertFromUtf8ToUtf16(const string& str) {
112112
return utf16String;
113113
}
114114

115-
uint16_t* Util::ConvertFromUtf8ToProtocolUtf16(const string& str) {
116-
auto utf16String =
117-
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().from_bytes(str);
118-
return (uint16_t*) utf16String.c_str();
119-
}
115+
//uint16_t* Util::ConvertFromUtf8ToProtocolUtf16(const string& str) {
116+
// auto utf16String =
117+
// std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().from_bytes(str);
118+
// return (uint16_t *) utf16String.c_str();
119+
//}
120120

121121
void Util::JoinString(const std::vector<std::string>& list, const std::string& delimiter,
122122
std::string& out) {
123123
out.clear();
124124

125125
stringstream ss;
126126

127-
for (vector<string>::const_iterator it = list.begin(); it != list.end(); ++it) {
127+
for (auto it = list.begin(); it != list.end(); ++it) {
128128
ss << *it;
129129

130130
if (it != list.end() - 1) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Util {
2424

2525
static std::u16string ConvertFromUtf8ToUtf16(const std::string& str);
2626

27-
static std::uint16_t* ConvertFromUtf8ToProtocolUtf16(const std::string& str);
27+
// static std::uint16_t* ConvertFromUtf8ToProtocolUtf16(const std::string& str);
2828
};
2929
}
3030

0 commit comments

Comments
 (0)