Skip to content

Commit 642a257

Browse files
committed
Ladybird/Android: Explicitly schedule Core::EventLoop in main activity
Instead of having an annoying loop that constantly reschedules a Core::EventLoop trigger, have the ALooperEventLoopManager do it itself in the did_post_event() function. We cannot simply re-use the Unix implementation directly because that implementation expects to actually be called all the time in order to service timers. If you don't call its' pump() method, timers do not get triggered. So, we do still need the seconary thread for Timers that was added earlier.
1 parent ff0494c commit 642a257

File tree

9 files changed

+96
-85
lines changed

9 files changed

+96
-85
lines changed

Ladybird/Android/src/main/cpp/ALooperEventLoopImplementation.cpp

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ static ALooperEventLoopImplementation& current_impl()
2727
return verify_cast<ALooperEventLoopImplementation>(Core::EventLoop::current().impl());
2828
}
2929

30-
ALooperEventLoopManager::ALooperEventLoopManager(JavaVM* vm, jobject timer_service)
31-
: m_vm(vm)
32-
, m_timer_service(timer_service)
30+
static int looper_callback(int fd, int events, void* data);
31+
32+
ALooperEventLoopManager::ALooperEventLoopManager(jobject timer_service)
33+
: m_timer_service(timer_service)
3334
{
34-
JavaEnvironment env(m_vm);
35+
JavaEnvironment env(global_vm);
3536

3637
jclass timer_class = env.get()->FindClass("org/serenityos/ladybird/TimerExecutorService$Timer");
3738
if (!timer_class)
@@ -53,14 +54,30 @@ ALooperEventLoopManager::ALooperEventLoopManager(JavaVM* vm, jobject timer_servi
5354
if (!m_unregister_timer)
5455
TODO();
5556
env.get()->DeleteLocalRef(timer_service_class);
57+
58+
auto ret = pipe2(m_pipe, O_CLOEXEC | O_NONBLOCK);
59+
VERIFY(ret == 0);
60+
61+
m_main_looper = ALooper_forThread();
62+
VERIFY(m_main_looper);
63+
ALooper_acquire(m_main_looper);
64+
65+
ret = ALooper_addFd(m_main_looper, m_pipe[0], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, &looper_callback, this);
66+
VERIFY(ret == 1);
5667
}
5768

5869
ALooperEventLoopManager::~ALooperEventLoopManager()
5970
{
60-
JavaEnvironment env(m_vm);
71+
JavaEnvironment env(global_vm);
6172

6273
env.get()->DeleteGlobalRef(m_timer_service);
6374
env.get()->DeleteGlobalRef(m_timer_class);
75+
76+
ALooper_removeFd(m_main_looper, m_pipe[0]);
77+
ALooper_release(m_main_looper);
78+
79+
::close(m_pipe[0]);
80+
::close(m_pipe[1]);
6481
}
6582

6683
NonnullOwnPtr<Core::EventLoopImplementation> ALooperEventLoopManager::make_implementation()
@@ -70,7 +87,7 @@ NonnullOwnPtr<Core::EventLoopImplementation> ALooperEventLoopManager::make_imple
7087

7188
int ALooperEventLoopManager::register_timer(Core::EventReceiver& receiver, int milliseconds, bool should_reload, Core::TimerShouldFireWhenNotVisible visibility)
7289
{
73-
JavaEnvironment env(m_vm);
90+
JavaEnvironment env(global_vm);
7491
auto& thread_data = EventLoopThreadData::the();
7592

7693
auto timer = env.get()->NewObject(m_timer_class, m_timer_constructor, reinterpret_cast<long>(&current_impl()));
@@ -87,7 +104,7 @@ int ALooperEventLoopManager::register_timer(Core::EventReceiver& receiver, int m
87104
bool ALooperEventLoopManager::unregister_timer(int timer_id)
88105
{
89106
if (auto timer = EventLoopThreadData::the().timers.take(timer_id); timer.has_value()) {
90-
JavaEnvironment env(m_vm);
107+
JavaEnvironment env(global_vm);
91108
return env.get()->CallBooleanMethod(m_timer_service, m_unregister_timer, timer_id);
92109
}
93110
return false;
@@ -107,29 +124,34 @@ void ALooperEventLoopManager::unregister_notifier(Core::Notifier& notifier)
107124

108125
void ALooperEventLoopManager::did_post_event()
109126
{
110-
current_impl().poke();
127+
int msg = 0xCAFEBABE;
128+
(void)write(m_pipe[1], &msg, sizeof(msg));
129+
}
130+
131+
int looper_callback(int fd, int events, void* data)
132+
{
133+
auto& manager = *static_cast<ALooperEventLoopManager*>(data);
134+
135+
if (events & ALOOPER_EVENT_INPUT) {
136+
int msg = 0;
137+
while (read(fd, &msg, sizeof(msg)) == sizeof(msg)) {
138+
// Do nothing, we don't actually care what the message was, just that it was posted
139+
}
140+
manager.on_did_post_event();
141+
}
142+
return 1;
111143
}
112144

113145
ALooperEventLoopImplementation::ALooperEventLoopImplementation()
114146
: m_event_loop(ALooper_prepare(0))
115147
, m_thread_data(&EventLoopThreadData::the())
116148
{
117-
auto ret = pipe2(m_pipe, O_CLOEXEC | O_NONBLOCK);
118-
VERIFY(ret == 0);
119-
120149
ALooper_acquire(m_event_loop);
121-
122-
ret = ALooper_addFd(m_event_loop, m_pipe[0], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, &ALooperEventLoopImplementation::looper_callback, this);
123-
VERIFY(ret == 1);
124150
}
125151

126152
ALooperEventLoopImplementation::~ALooperEventLoopImplementation()
127153
{
128-
ALooper_removeFd(m_event_loop, m_pipe[0]);
129154
ALooper_release(m_event_loop);
130-
131-
::close(m_pipe[0]);
132-
::close(m_pipe[1]);
133155
}
134156

135157
EventLoopThreadData& ALooperEventLoopImplementation::thread_data()
@@ -181,26 +203,6 @@ void ALooperEventLoopImplementation::post_event(Core::EventReceiver& receiver, N
181203
wake();
182204
}
183205

184-
int ALooperEventLoopImplementation::looper_callback(int fd, int events, void* data)
185-
{
186-
auto& impl = *static_cast<ALooperEventLoopImplementation*>(data);
187-
(void)impl; // FIXME: Do we need to do anything with the instance here?
188-
189-
if (events & ALOOPER_EVENT_INPUT) {
190-
int msg = 0;
191-
while (read(fd, &msg, sizeof(msg)) == sizeof(msg)) {
192-
// Do nothing, we don't actually care what the message was, just that it was posted
193-
}
194-
}
195-
return 1;
196-
}
197-
198-
void ALooperEventLoopImplementation::poke()
199-
{
200-
int msg = 0xCAFEBABE;
201-
(void)write(m_pipe[1], &msg, sizeof(msg));
202-
}
203-
204206
static int notifier_callback(int fd, int, void* data)
205207
{
206208
auto& notifier = *static_cast<Core::Notifier*>(data);
@@ -211,7 +213,7 @@ static int notifier_callback(int fd, int, void* data)
211213
notifier.dispatch_event(event);
212214

213215
// Wake up from ALooper_pollAll, and service this event on the event queue
214-
current_impl().poke();
216+
current_impl().wake();
215217

216218
return 1;
217219
}

Ladybird/Android/src/main/cpp/ALooperEventLoopImplementation.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Ladybird {
1919

2020
class ALooperEventLoopManager : public Core::EventLoopManager {
2121
public:
22-
ALooperEventLoopManager(JavaVM*, jobject timer_service);
22+
ALooperEventLoopManager(jobject timer_service);
2323
virtual ~ALooperEventLoopManager() override;
2424
virtual NonnullOwnPtr<Core::EventLoopImplementation> make_implementation() override;
2525

@@ -31,12 +31,15 @@ class ALooperEventLoopManager : public Core::EventLoopManager {
3131

3232
virtual void did_post_event() override;
3333

34+
Function<void()> on_did_post_event;
35+
3436
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
3537
virtual int register_signal(int, Function<void(int)>) override { return 0; }
3638
virtual void unregister_signal(int) override { }
3739

3840
private:
39-
JavaVM* m_vm { nullptr };
41+
int m_pipe[2] = {};
42+
ALooper* m_main_looper { nullptr };
4043
jobject m_timer_service { nullptr };
4144
jmethodID m_register_timer { nullptr };
4245
jmethodID m_unregister_timer { nullptr };
@@ -74,22 +77,17 @@ class ALooperEventLoopImplementation : public Core::EventLoopImplementation {
7477
virtual bool was_exit_requested() const override { return false; }
7578
virtual void notify_forked_and_in_child() override { }
7679

77-
void poke();
78-
7980
EventLoopThreadData& thread_data();
8081

8182
private:
8283
friend class ALooperEventLoopManager;
8384

8485
ALooperEventLoopImplementation();
8586

86-
static int looper_callback(int fd, int events, void* data);
87-
8887
void register_notifier(Core::Notifier&);
8988
void unregister_notifier(Core::Notifier&);
9089

9190
ALooper* m_event_loop { nullptr };
92-
int m_pipe[2] {};
9391
int m_exit_code { 0 };
9492
Atomic<bool> m_exit_requested { false };
9593
EventLoopThreadData* m_thread_data { nullptr };

Ladybird/Android/src/main/cpp/JNIHelpers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ class JavaEnvironment {
4141
JNIEnv* m_env = nullptr;
4242
bool m_did_attach_thread = false;
4343
};
44+
45+
extern JavaVM* global_vm;

Ladybird/Android/src/main/cpp/LadybirdActivity.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include "ALooperEventLoopImplementation.h"
8+
#include "JNIHelpers.h"
89
#include <AK/DeprecatedString.h>
910
#include <AK/Format.h>
1011
#include <AK/HashMap.h>
@@ -22,10 +23,13 @@
2223

2324
static ErrorOr<void> extract_tar_archive(String archive_file, DeprecatedString output_directory);
2425

26+
JavaVM* global_vm;
2527
static OwnPtr<Core::EventLoop> s_main_event_loop;
28+
static jobject s_java_instance;
29+
static jmethodID s_schedule_event_loop_method;
2630

2731
extern "C" JNIEXPORT void JNICALL
28-
Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jstring tag_name, jobject timer_service)
32+
Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobject thiz, jstring resource_dir, jstring tag_name, jobject timer_service)
2933
{
3034
char const* raw_resource_dir = env->GetStringUTFChars(resource_dir, nullptr);
3135
s_serenity_resource_root = raw_resource_dir;
@@ -46,18 +50,43 @@ Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobjec
4650
dbgln("Hopefully no developer changed the asset files and expected them to be re-extracted!");
4751
}
4852

53+
env->GetJavaVM(&global_vm);
54+
VERIFY(global_vm);
55+
56+
s_java_instance = env->NewGlobalRef(thiz);
57+
jclass clazz = env->GetObjectClass(s_java_instance);
58+
VERIFY(clazz);
59+
s_schedule_event_loop_method = env->GetMethodID(clazz, "scheduleEventLoop", "()V");
60+
VERIFY(s_schedule_event_loop_method);
61+
env->DeleteLocalRef(clazz);
62+
4963
jobject timer_service_ref = env->NewGlobalRef(timer_service);
50-
JavaVM* vm = nullptr;
51-
jint ret = env->GetJavaVM(&vm);
52-
VERIFY(ret == 0);
53-
Core::EventLoopManager::install(*new Ladybird::ALooperEventLoopManager(vm, timer_service_ref));
64+
65+
auto* event_loop_manager = new Ladybird::ALooperEventLoopManager(timer_service_ref);
66+
event_loop_manager->on_did_post_event = [] {
67+
JavaEnvironment env(global_vm);
68+
env.get()->CallVoidMethod(s_java_instance, s_schedule_event_loop_method);
69+
};
70+
Core::EventLoopManager::install(*event_loop_manager);
5471
s_main_event_loop = make<Core::EventLoop>();
5572
}
5673

5774
extern "C" JNIEXPORT void JNICALL
5875
Java_org_serenityos_ladybird_LadybirdActivity_execMainEventLoop(JNIEnv*, jobject /* thiz */)
5976
{
60-
s_main_event_loop->pump(Core::EventLoop::WaitMode::PollForEvents);
77+
if (s_main_event_loop) {
78+
s_main_event_loop->pump(Core::EventLoop::WaitMode::PollForEvents);
79+
}
80+
}
81+
82+
extern "C" JNIEXPORT void JNICALL
83+
Java_org_serenityos_ladybird_LadybirdActivity_disposeNativeCode(JNIEnv* env, jobject /* thiz */)
84+
{
85+
s_main_event_loop = nullptr;
86+
s_schedule_event_loop_method = nullptr;
87+
env->DeleteGlobalRef(s_java_instance);
88+
89+
delete &Core::EventLoopManager::the();
6190
}
6291

6392
ErrorOr<void> extract_tar_archive(String archive_file, DeprecatedString output_directory)

Ladybird/Android/src/main/cpp/LadybirdServiceBase.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,3 @@
1010
#include <jni.h>
1111

1212
ErrorOr<int> service_main(int ipc_socket, int fd_passing_socket);
13-
14-
extern JavaVM* global_vm;

Ladybird/Android/src/main/cpp/TimerExecutorService.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,7 @@ Java_org_serenityos_ladybird_TimerExecutorService_00024Timer_nativeRun(JNIEnv*,
2929
event_loop_impl.post_event(*receiver, make<Core::TimerEvent>(id));
3030
}
3131
// Flush the event loop on this thread to keep any garbage from building up
32-
s_event_loop.pump(Core::EventLoop::WaitMode::PollForEvents);
32+
if (auto num_events = s_event_loop.pump(Core::EventLoop::WaitMode::PollForEvents); num_events != 0) {
33+
dbgln("BUG: Processed {} events on Timer thread!", num_events);
34+
}
3335
}

Ladybird/Android/src/main/cpp/WebViewImplementationNative.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ class WebViewImplementationNative : public WebView::ViewImplementation {
3232
static jclass global_class_reference;
3333
static jmethodID bind_webcontent_method;
3434
static jmethodID invalidate_layout_method;
35-
static JavaVM* global_vm;
3635

3736
jobject java_instance() const { return m_java_instance; }
3837

Ladybird/Android/src/main/cpp/WebViewImplementationNativeJNI.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,10 @@ using namespace Ladybird;
1212
jclass WebViewImplementationNative::global_class_reference;
1313
jmethodID WebViewImplementationNative::bind_webcontent_method;
1414
jmethodID WebViewImplementationNative::invalidate_layout_method;
15-
JavaVM* WebViewImplementationNative::global_vm;
1615

1716
extern "C" JNIEXPORT void JNICALL
1817
Java_org_serenityos_ladybird_WebViewImplementation_00024Companion_nativeClassInit(JNIEnv* env, jobject /* thiz */)
1918
{
20-
auto ret = env->GetJavaVM(&WebViewImplementationNative::global_vm);
21-
if (ret != 0)
22-
TODO();
23-
2419
auto local_class = env->FindClass("org/serenityos/ladybird/WebViewImplementation");
2520
if (!local_class)
2621
TODO();

Ladybird/Android/src/main/java/org/serenityos/ladybird/LadybirdActivity.kt

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class LadybirdActivity : AppCompatActivity() {
1616

1717
private lateinit var binding: ActivityMainBinding
1818
private lateinit var resourceDir: String
19+
private lateinit var view: WebView
20+
private var timerService = TimerExecutorService()
1921

2022
override fun onCreate(savedInstanceState: Bundle?) {
2123
super.onCreate(savedInstanceState)
@@ -28,10 +30,6 @@ class LadybirdActivity : AppCompatActivity() {
2830
setSupportActionBar(binding.toolbar)
2931
view = binding.webView
3032
view.initialize(resourceDir)
31-
32-
mainExecutor.execute {
33-
callNativeEventLoopForever()
34-
}
3533
}
3634

3735
override fun onStart() {
@@ -44,31 +42,19 @@ class LadybirdActivity : AppCompatActivity() {
4442

4543
override fun onDestroy() {
4644
view.dispose()
45+
disposeNativeCode()
4746
super.onDestroy()
4847
}
4948

50-
private lateinit var view: WebView
51-
private var timerService = TimerExecutorService()
52-
53-
/**
54-
* A native method that is implemented by the 'ladybird' native library,
55-
* which is packaged with this application.
56-
*/
57-
private external fun initNativeCode(
58-
resourceDir: String,
59-
tag: String,
60-
timerService: TimerExecutorService
61-
)
62-
63-
// FIXME: Instead of doing this, can we push a message to the message queue of the java Looper
64-
// when an event is pushed to the main thread, and use that to clear out the
65-
// Core::ThreadEventQueues?
66-
private fun callNativeEventLoopForever() {
67-
execMainEventLoop()
68-
mainExecutor.execute { callNativeEventLoopForever() }
49+
private fun scheduleEventLoop() {
50+
mainExecutor.execute {
51+
execMainEventLoop()
52+
}
6953
}
7054

71-
private external fun execMainEventLoop();
55+
private external fun initNativeCode(resourceDir: String, tag: String, timerService: TimerExecutorService)
56+
private external fun disposeNativeCode()
57+
private external fun execMainEventLoop()
7258

7359
companion object {
7460
// Used to load the 'ladybird' library on application startup.

0 commit comments

Comments
 (0)