1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include "tonic/dart_microtask_queue.h" |
6 | |
7 | #include "tonic/common/build_config.h" |
8 | #include "tonic/dart_state.h" |
9 | #include "tonic/logging/dart_invoke.h" |
10 | |
11 | #ifdef OS_IOS |
12 | #include <pthread.h> |
13 | #endif |
14 | |
15 | namespace tonic { |
16 | namespace { |
17 | |
18 | #ifdef OS_IOS |
19 | // iOS doesn't support the thread_local keyword. |
20 | |
21 | pthread_key_t g_queue_key; |
22 | pthread_once_t g_queue_key_once = PTHREAD_ONCE_INIT; |
23 | |
24 | void MakeKey() { |
25 | pthread_key_create(&g_queue_key, nullptr); |
26 | } |
27 | |
28 | void SetQueue(DartMicrotaskQueue* queue) { |
29 | pthread_once(&g_queue_key_once, MakeKey); |
30 | pthread_setspecific(g_queue_key, queue); |
31 | } |
32 | |
33 | DartMicrotaskQueue* GetQueue() { |
34 | return static_cast<tonic::DartMicrotaskQueue*>( |
35 | pthread_getspecific(g_queue_key)); |
36 | } |
37 | |
38 | #else |
39 | |
40 | thread_local DartMicrotaskQueue* g_queue = nullptr; |
41 | |
42 | void SetQueue(DartMicrotaskQueue* queue) { |
43 | g_queue = queue; |
44 | } |
45 | |
46 | DartMicrotaskQueue* GetQueue() { |
47 | return g_queue; |
48 | } |
49 | |
50 | #endif |
51 | |
52 | } // namespace |
53 | |
54 | DartMicrotaskQueue::DartMicrotaskQueue() : last_error_(kNoError) {} |
55 | |
56 | DartMicrotaskQueue::~DartMicrotaskQueue() = default; |
57 | |
58 | void DartMicrotaskQueue::StartForCurrentThread() { |
59 | SetQueue(new DartMicrotaskQueue()); |
60 | } |
61 | |
62 | DartMicrotaskQueue* DartMicrotaskQueue::GetForCurrentThread() { |
63 | return GetQueue(); |
64 | } |
65 | |
66 | void DartMicrotaskQueue::ScheduleMicrotask(Dart_Handle callback) { |
67 | queue_.emplace_back(DartState::Current(), callback); |
68 | } |
69 | |
70 | void DartMicrotaskQueue::RunMicrotasks() { |
71 | while (!queue_.empty()) { |
72 | MicrotaskQueue local; |
73 | std::swap(queue_, local); |
74 | for (const auto& callback : local) { |
75 | if (auto dart_state = callback.dart_state().lock()) { |
76 | DartState::Scope dart_scope(dart_state.get()); |
77 | Dart_Handle result = Dart_InvokeClosure(callback.value(), 0, nullptr); |
78 | // If the Dart program has set a return code, then it is intending to |
79 | // shut down by way of a fatal error, and so there is no need to emit a |
80 | // log message. |
81 | if (!dart_state->has_set_return_code() || !Dart_IsError(result) || |
82 | !Dart_IsFatalError(result)) { |
83 | LogIfError(result); |
84 | } |
85 | DartErrorHandleType error = GetErrorHandleType(result); |
86 | if (error != kNoError) { |
87 | last_error_ = error; |
88 | } |
89 | dart_state->MessageEpilogue(result); |
90 | if (!Dart_CurrentIsolate()) |
91 | return; |
92 | } |
93 | } |
94 | } |
95 | } |
96 | |
97 | void DartMicrotaskQueue::Destroy() { |
98 | TONIC_DCHECK(this == GetForCurrentThread()); |
99 | SetQueue(nullptr); |
100 | delete this; |
101 | } |
102 | |
103 | DartErrorHandleType DartMicrotaskQueue::GetLastError() { |
104 | return last_error_; |
105 | } |
106 | |
107 | } // namespace tonic |
108 |