// 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 #endif // OS_IOS 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( 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