Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flush RN task queue with invokeAsync #4389

Merged
merged 6 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions react-native/ios/RealmReact/RealmReact.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#import <React/RCTBridge+Private.h>
#import <React/RCTJavaScriptExecutor.h>
#include <ReactCommon/CallInvoker.h>

#import <objc/runtime.h>
#import <arpa/inet.h>
Expand Down Expand Up @@ -51,6 +52,7 @@ - (JSContext *)context;
@interface RCTBridge (Realm_RCTCxxBridge)
- (JSGlobalContextRef)jsContextRef;
- (void *)runtime;
- (std::shared_ptr<facebook::react::CallInvoker>)jsCallInvoker;
@end

extern "C" JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool create) {
Expand Down Expand Up @@ -83,6 +85,7 @@ @interface RealmReact () <RCTBridgeModule>

@implementation RealmReact {
NSMutableDictionary *_eventHandlers;
bool waitingForFlush;

#if DEBUG
GCDWebServer *_webServer;
Expand All @@ -108,6 +111,7 @@ - (instancetype)init {
self = [super init];
if (self) {
_eventHandlers = [[NSMutableDictionary alloc] init];
waitingForFlush = false;
}
return self;
}
Expand Down Expand Up @@ -274,15 +278,15 @@ - (void)dealloc {

typedef JSGlobalContextRef (^JSContextRefExtractor)();

void _initializeOnJSThread(JSContextRefExtractor jsContextExtractor) {
void _initializeOnJSThread(JSContextRefExtractor jsContextExtractor, std::function<void()> flushUiQueue) {
// Make sure the previous JS thread is completely finished before continuing.
static __weak NSThread *s_currentJSThread;
while (s_currentJSThread && !s_currentJSThread.finished) {
[NSThread sleepForTimeInterval:0.1];
}
s_currentJSThread = [NSThread currentThread];

RJSInitializeInContext(jsContextExtractor());
RJSInitializeInContext(jsContextExtractor(), flushUiQueue);
}

- (void)setBridge:(RCTBridge *)bridge {
Expand Down Expand Up @@ -310,7 +314,7 @@ - (void)setBridge:(RCTBridge *)bridge {
if (!self || !bridge) {
return;
}

_initializeOnJSThread(^{
// RN < 0.58 has a private method that returns the js context
if ([bridge respondsToSelector:@selector(jsContextRef)]) {
Expand All @@ -325,6 +329,17 @@ - (void)setBridge:(RCTBridge *)bridge {
JSGlobalContextRef ctx_;
};
return static_cast<RealmJSCRuntime*>(bridge.runtime)->ctx_;
}, ^{
if (!waitingForFlush) {
waitingForFlush = true;
// Calling jsCallInvokver->invokeAsync restuls in React Native flushing any pending UI
// updates after we have called into JS from C++
//
// TODO add a bit more info
[bridge jsCallInvoker]->invokeAsync([&](){
waitingForFlush = false;
tomduncalf marked this conversation as resolved.
Show resolved Hide resolved
});
}
});
} queue:RCTJSThread];
} else { // React Native 0.44 and older
Expand All @@ -341,6 +356,8 @@ - (void)setBridge:(RCTBridge *)bridge {

_initializeOnJSThread(^ {
return RealmReactGetJSGlobalContextForExecutor(executor, true);
}, [&]() {
// jsCallInvoker does not exist on older RN
});
}];
}
Expand Down
5 changes: 4 additions & 1 deletion src/jsc/jsc_class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "jsc_types.hpp"
#include "jsc_function.hpp"

#include "js_class.hpp"
#include "js_util.hpp"
Expand All @@ -41,7 +42,7 @@ extern js::Protected<JSObjectRef> FunctionPrototype;
extern js::Protected<JSObjectRef> RealmObjectClassConstructor;
extern js::Protected<JSObjectRef> RealmObjectClassConstructorPrototype;

static inline void jsc_class_init(JSContextRef ctx, JSObjectRef globalObject)
static inline void jsc_class_init(JSContextRef ctx, JSObjectRef globalObject, std::function<void()> flushUiQueue)
{
// handle ReactNative app refresh by reseting the cached constructor values
if (RealmObjectClassConstructor) {
Expand All @@ -63,6 +64,8 @@ static inline void jsc_class_init(JSContextRef ctx, JSObjectRef globalObject)
JSObjectRef globalFunction = jsc::Value::to_object(ctx, value);
value = jsc::Object::get_property(ctx, globalFunction, "prototype");
FunctionPrototype = js::Protected<JSObjectRef>(ctx, Value::to_object(ctx, value));

js::flush_ui_queue = flushUiQueue;
}

template <typename T>
Expand Down
9 changes: 9 additions & 0 deletions src/jsc/jsc_function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@
namespace realm {
namespace js {

// TODO
extern std::function<void()> flush_ui_queue;

template <>
inline JSValueRef jsc::Function::call(JSContextRef ctx, const JSObjectRef& function, const JSObjectRef& this_object,
size_t argc, const JSValueRef arguments[])
{
JSValueRef exception = nullptr;
JSValueRef result = JSObjectCallAsFunction(ctx, function, this_object, argc, arguments, &exception);

flush_ui_queue();

if (exception) {
throw jsc::Exception(ctx, exception);
}
Expand All @@ -48,6 +54,9 @@ inline JSObjectRef jsc::Function::construct(JSContextRef ctx, const JSObjectRef&
{
JSValueRef exception = nullptr;
JSObjectRef result = JSObjectCallAsConstructor(ctx, function, argc, arguments, &exception);

flush_ui_queue();

if (exception) {
throw jsc::Exception(ctx, exception);
}
Expand Down
8 changes: 6 additions & 2 deletions src/jsc/jsc_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ js::Protected<JSObjectRef> FunctionPrototype;
js::Protected<JSObjectRef> RealmObjectClassConstructor;
js::Protected<JSObjectRef> RealmObjectClassConstructorPrototype;
} // namespace jsc

namespace js {
std::function<void()> flush_ui_queue;
} // namespace js
} // namespace realm

extern "C" {
Expand All @@ -43,13 +47,13 @@ JSObjectRef RJSConstructorCreate(JSContextRef ctx)
return js::RealmClass<Types>::create_constructor(ctx);
}

void RJSInitializeInContext(JSContextRef ctx)
void RJSInitializeInContext(JSContextRef ctx, std::function<void()> flush_ui_queue)
{
static const jsc::String realm_string = "Realm";

JSObjectRef global_object = JSContextGetGlobalObject(ctx);

jsc_class_init(ctx, global_object);
jsc_class_init(ctx, global_object, flush_ui_queue);

JSObjectRef realm_constructor = RJSConstructorCreate(ctx);

Expand Down
3 changes: 2 additions & 1 deletion src/jsc/jsc_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
#pragma once

#include <JavaScriptCore/JSBase.h>
#include <functional>

#ifdef __cplusplus
extern "C" {
#endif

JSObjectRef RJSConstructorCreate(JSContextRef ctx);
void RJSInitializeInContext(JSContextRef ctx);
void RJSInitializeInContext(JSContextRef ctx, std::function<void()> send_dummy_event);
void RJSInvalidateCaches();

#ifdef __cplusplus
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ RPCServerImpl::RPCServerImpl()
}

m_requests["/create_session"] = [this](const json dict) {
RJSInitializeInContext(m_context);
RJSInitializeInContext(m_context, []() {});

jsc::String realm_string = "Realm";
JSObjectRef realm_constructor =
Expand Down