| // 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. |
| |
| #import "flutter/shell/platform/darwin/ios/platform_message_handler_ios.h" |
| |
| #import "flutter/fml/trace_event.h" |
| #import "flutter/lib/ui/window/platform_message.h" |
| #import "flutter/shell/platform/darwin/common/buffer_conversions.h" |
| #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" |
| |
| static uint64_t platform_message_counter = 1; |
| |
| @interface FLTSerialTaskQueue : NSObject <FlutterTaskQueue> |
| @property(nonatomic, strong) dispatch_queue_t queue; |
| @end |
| |
| @implementation FLTSerialTaskQueue |
| - (instancetype)init { |
| self = [super init]; |
| if (self) { |
| _queue = dispatch_queue_create("FLTSerialTaskQueue", DISPATCH_QUEUE_SERIAL); |
| } |
| return self; |
| } |
| |
| - (void)dealloc { |
| dispatch_release(_queue); |
| [super dealloc]; |
| } |
| |
| - (void)dispatch:(dispatch_block_t)block { |
| dispatch_async(self.queue, block); |
| } |
| @end |
| |
| namespace flutter { |
| |
| NSObject<FlutterTaskQueue>* PlatformMessageHandlerIos::MakeBackgroundTaskQueue() { |
| return [[[FLTSerialTaskQueue alloc] init] autorelease]; |
| } |
| |
| PlatformMessageHandlerIos::PlatformMessageHandlerIos( |
| fml::RefPtr<fml::TaskRunner> platform_task_runner) |
| : platform_task_runner_(std::move(platform_task_runner)) {} |
| |
| void PlatformMessageHandlerIos::HandlePlatformMessage(std::unique_ptr<PlatformMessage> message) { |
| // This can be called from any isolate's thread. |
| @autoreleasepool { |
| fml::RefPtr<flutter::PlatformMessageResponse> completer = message->response(); |
| HandlerInfo handler_info; |
| { |
| // TODO(gaaclarke): This mutex is a bottleneck for multiple isolates sending |
| // messages at the same time. This could be potentially changed to a |
| // read-write lock. |
| std::lock_guard lock(message_handlers_mutex_); |
| auto it = message_handlers_.find(message->channel()); |
| if (it != message_handlers_.end()) { |
| handler_info = it->second; |
| } |
| } |
| if (handler_info.handler) { |
| FlutterBinaryMessageHandler handler = handler_info.handler; |
| NSData* data = nil; |
| if (message->hasData()) { |
| data = ConvertMappingToNSData(message->releaseData()); |
| } |
| |
| uint64_t platform_message_id = platform_message_counter++; |
| TRACE_EVENT_ASYNC_BEGIN1("flutter", "PlatformChannel ScheduleHandler", platform_message_id, |
| "channel", message->channel().c_str()); |
| dispatch_block_t run_handler = ^{ |
| handler(data, ^(NSData* reply) { |
| TRACE_EVENT_ASYNC_END0("flutter", "PlatformChannel ScheduleHandler", platform_message_id); |
| // Called from any thread. |
| if (completer) { |
| if (reply) { |
| completer->Complete(ConvertNSDataToMappingPtr(reply)); |
| } else { |
| completer->CompleteEmpty(); |
| } |
| } |
| }); |
| }; |
| |
| if (handler_info.task_queue.get()) { |
| [handler_info.task_queue.get() dispatch:run_handler]; |
| } else { |
| dispatch_async(dispatch_get_main_queue(), run_handler); |
| } |
| } else { |
| if (completer) { |
| completer->CompleteEmpty(); |
| } |
| } |
| } |
| } |
| |
| bool PlatformMessageHandlerIos::DoesHandlePlatformMessageOnPlatformThread() const { |
| return false; |
| } |
| |
| void PlatformMessageHandlerIos::InvokePlatformMessageResponseCallback( |
| int response_id, |
| std::unique_ptr<fml::Mapping> mapping) { |
| // Called from any thread. |
| // TODO(gaaclarke): This vestigal from the Android implementation, find a way |
| // to migrate this to PlatformMessageHandlerAndroid. |
| } |
| |
| void PlatformMessageHandlerIos::InvokePlatformMessageEmptyResponseCallback(int response_id) { |
| // Called from any thread. |
| // TODO(gaaclarke): This vestigal from the Android implementation, find a way |
| // to migrate this to PlatformMessageHandlerAndroid. |
| } |
| |
| void PlatformMessageHandlerIos::SetMessageHandler(const std::string& channel, |
| FlutterBinaryMessageHandler handler, |
| NSObject<FlutterTaskQueue>* task_queue) { |
| FML_CHECK(platform_task_runner_->RunsTasksOnCurrentThread()); |
| /// TODO(gaaclarke): This should be migrated to a lockfree datastructure. |
| std::lock_guard lock(message_handlers_mutex_); |
| message_handlers_.erase(channel); |
| if (handler) { |
| message_handlers_[channel] = { |
| .task_queue = fml::scoped_nsprotocol([task_queue retain]), |
| .handler = |
| fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain}, |
| }; |
| } |
| } |
| } // namespace flutter |