Avoid copying the contents of large platform message responses (flutter/engine#4947)

Assets are loaded via platform messages, and currently asset payloads are
being copied into Dart typed data buffers.  This change uses external
typed data objects that wrap the existing buffer if copying would be
expensive.

See https://github.com/flutter/flutter/issues/16291
This commit is contained in:
Jason Simmons 2018-04-10 12:15:58 -07:00 committed by GitHub
parent 679a670c34
commit d7dd122a5e
3 changed files with 49 additions and 30 deletions

View File

@ -7,12 +7,41 @@
#include <utility>
#include "flutter/common/threads.h"
#include "flutter/lib/ui/window/window.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/tonic/dart_state.h"
#include "lib/tonic/logging/dart_invoke.h"
namespace blink {
namespace {
// Avoid copying the contents of messages beyond a certain size.
const int kMessageCopyThreshold = 1000;
void MessageDataFinalizer(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
std::vector<uint8_t>* data = reinterpret_cast<std::vector<uint8_t>*>(peer);
delete data;
}
Dart_Handle WrapByteData(std::vector<uint8_t> data) {
if (data.size() < kMessageCopyThreshold) {
return ToByteData(data);
} else {
std::vector<uint8_t>* heap_data = new std::vector<uint8_t>(std::move(data));
Dart_Handle data_handle = Dart_NewExternalTypedData(
Dart_TypedData_kByteData, heap_data->data(), heap_data->size());
DART_CHECK_VALID(data_handle);
Dart_NewWeakPersistentHandle(data_handle, heap_data, heap_data->size(),
MessageDataFinalizer);
return data_handle;
}
}
} // anonymous namespace
PlatformMessageResponseDart::PlatformMessageResponseDart(
tonic::DartPersistentValue callback)
: callback_(std::move(callback)) {}
@ -38,19 +67,7 @@ void PlatformMessageResponseDart::Complete(std::vector<uint8_t> data) {
return;
tonic::DartState::Scope scope(dart_state);
Dart_Handle byte_buffer =
Dart_NewTypedData(Dart_TypedData_kByteData, data.size());
DART_CHECK_VALID(byte_buffer);
void* buffer;
intptr_t length;
Dart_TypedData_Type type;
DART_CHECK_VALID(
Dart_TypedDataAcquireData(byte_buffer, &type, &buffer, &length));
FXL_CHECK(type == Dart_TypedData_kByteData);
FXL_CHECK(static_cast<size_t>(length) == data.size());
memcpy(buffer, data.data(), length);
Dart_TypedDataReleaseData(byte_buffer);
Dart_Handle byte_buffer = WrapByteData(std::move(data));
tonic::DartInvoke(callback.Release(), {byte_buffer});
}));
}

View File

@ -22,23 +22,6 @@ using tonic::ToDart;
namespace blink {
namespace {
Dart_Handle ToByteData(const std::vector<uint8_t>& buffer) {
Dart_Handle data_handle =
Dart_NewTypedData(Dart_TypedData_kByteData, buffer.size());
if (Dart_IsError(data_handle))
return data_handle;
Dart_TypedData_Type type;
void* data = nullptr;
intptr_t num_bytes = 0;
FXL_CHECK(!Dart_IsError(
Dart_TypedDataAcquireData(data_handle, &type, &data, &num_bytes)));
memcpy(data, buffer.data(), num_bytes);
Dart_TypedDataReleaseData(data_handle);
return data_handle;
}
void DefaultRouteName(Dart_NativeArguments args) {
std::string routeName =
UIDartState::Current()->window()->client()->DefaultRouteName();
@ -119,6 +102,23 @@ void _RespondToPlatformMessage(Dart_NativeArguments args) {
} // namespace
Dart_Handle ToByteData(const std::vector<uint8_t>& buffer) {
Dart_Handle data_handle =
Dart_NewTypedData(Dart_TypedData_kByteData, buffer.size());
if (Dart_IsError(data_handle))
return data_handle;
Dart_TypedData_Type type;
void* data = nullptr;
intptr_t num_bytes = 0;
FXL_CHECK(!Dart_IsError(
Dart_TypedDataAcquireData(data_handle, &type, &data, &num_bytes)));
memcpy(data, buffer.data(), num_bytes);
Dart_TypedDataReleaseData(data_handle);
return data_handle;
}
WindowClient::~WindowClient() {}
Window::Window(WindowClient* client) : client_(client) {}

View File

@ -21,6 +21,8 @@ class DartLibraryNatives;
namespace blink {
class Scene;
Dart_Handle ToByteData(const std::vector<uint8_t>& buffer);
class WindowClient {
public:
virtual std::string DefaultRouteName() = 0;