| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tonic/dart_microtask_queue.h" |
| |
| #include "tonic/common/build_config.h" |
| #include "tonic/dart_state.h" |
| #include "tonic/logging/dart_invoke.h" |
| |
| #ifdef OS_IOS |
| #include <pthread.h> |
| #endif |
| |
| namespace tonic { |
| namespace { |
| |
| #ifdef OS_IOS |
| // iOS doesn't support the thread_local keyword. |
| |
| pthread_key_t g_queue_key; |
| pthread_once_t g_queue_key_once = PTHREAD_ONCE_INIT; |
| |
| void MakeKey() { |
| pthread_key_create(&g_queue_key, nullptr); |
| } |
| |
| void SetQueue(DartMicrotaskQueue* queue) { |
| pthread_once(&g_queue_key_once, MakeKey); |
| pthread_setspecific(g_queue_key, queue); |
| } |
| |
| DartMicrotaskQueue* GetQueue() { |
| return static_cast<tonic::DartMicrotaskQueue*>( |
| pthread_getspecific(g_queue_key)); |
| } |
| |
| #else |
| |
| thread_local DartMicrotaskQueue* g_queue = nullptr; |
| |
| void SetQueue(DartMicrotaskQueue* queue) { |
| g_queue = queue; |
| } |
| |
| DartMicrotaskQueue* GetQueue() { |
| return g_queue; |
| } |
| |
| #endif |
| |
| } // namespace |
| |
| DartMicrotaskQueue::DartMicrotaskQueue() : last_error_(kNoError) {} |
| |
| DartMicrotaskQueue::~DartMicrotaskQueue() = default; |
| |
| void DartMicrotaskQueue::StartForCurrentThread() { |
| SetQueue(new DartMicrotaskQueue()); |
| } |
| |
| DartMicrotaskQueue* DartMicrotaskQueue::GetForCurrentThread() { |
| return GetQueue(); |
| } |
| |
| void DartMicrotaskQueue::ScheduleMicrotask(Dart_Handle callback) { |
| queue_.emplace_back(DartState::Current(), callback); |
| } |
| |
| void DartMicrotaskQueue::RunMicrotasks() { |
| while (!queue_.empty()) { |
| MicrotaskQueue local; |
| std::swap(queue_, local); |
| for (const auto& callback : local) { |
| if (auto dart_state = callback.dart_state().lock()) { |
| DartState::Scope dart_scope(dart_state.get()); |
| Dart_Handle result = Dart_InvokeClosure(callback.value(), 0, nullptr); |
| // If the Dart program has set a return code, then it is intending to |
| // shut down by way of a fatal error, and so there is no need to emit a |
| // log message. |
| if (!dart_state->has_set_return_code() || !Dart_IsError(result) || |
| !Dart_IsFatalError(result)) { |
| LogIfError(result); |
| } |
| DartErrorHandleType error = GetErrorHandleType(result); |
| if (error != kNoError) { |
| last_error_ = error; |
| } |
| dart_state->MessageEpilogue(result); |
| if (!Dart_CurrentIsolate()) |
| return; |
| } |
| } |
| } |
| } |
| |
| void DartMicrotaskQueue::Destroy() { |
| TONIC_DCHECK(this == GetForCurrentThread()); |
| SetQueue(nullptr); |
| delete this; |
| } |
| |
| DartErrorHandleType DartMicrotaskQueue::GetLastError() { |
| return last_error_; |
| } |
| |
| } // namespace tonic |