Factor wire server handlers into proper command handlers and doers

Bug: dawn:88
Change-Id: If637e7ac694d68ad7a4a1e85fd241e8b48e62f6a
Reviewed-on: https://dawn-review.googlesource.com/c/4060
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2019-02-11 19:39:46 +00:00 committed by Commit Bot service account
parent 3b71e65658
commit 62e83971ca
12 changed files with 250 additions and 175 deletions

View File

@ -779,6 +779,7 @@ dawn_generator("libdawn_wire_gen") {
"dawn_wire/client/ClientPrototypes_autogen.inl",
"dawn_wire/server/ServerBase_autogen.h",
"dawn_wire/server/ServerCallbacks_autogen.cpp",
"dawn_wire/server/ServerDoers_autogen.cpp",
"dawn_wire/server/ServerHandlers_autogen.cpp",
"dawn_wire/server/ServerPrototypes_autogen.inl",
]

View File

@ -71,7 +71,7 @@
"server_custom_pre_handler_commands": [
"BufferUnmap"
],
"server_custom_post_handler_commands": [
"server_handwritten_commands": [
"QueueSignal"
],
"server_reverse_lookup_objects": [

View File

@ -311,12 +311,17 @@ def js_native_methods(types, typ):
def debug(text):
print(text)
def do_assert(expr):
assert expr
return ''
def get_renders_for_targets(api_params, wire_json, targets):
base_params = {
'enumerate': enumerate,
'format': format,
'len': len,
'debug': debug,
'assert': do_assert,
'Name': lambda name: Name(name),
@ -393,6 +398,7 @@ def get_renders_for_targets(api_params, wire_json, targets):
renders.append(FileRender('dawn_wire/client/ClientPrototypes.inl', 'dawn_wire/client/ClientPrototypes_autogen.inl', wire_params))
renders.append(FileRender('dawn_wire/server/ServerBase.h', 'dawn_wire/server/ServerBase_autogen.h', wire_params))
renders.append(FileRender('dawn_wire/server/ServerCallbacks.cpp', 'dawn_wire/server/ServerCallbacks_autogen.cpp', wire_params))
renders.append(FileRender('dawn_wire/server/ServerDoers.cpp', 'dawn_wire/server/ServerDoers_autogen.cpp', wire_params))
renders.append(FileRender('dawn_wire/server/ServerHandlers.cpp', 'dawn_wire/server/ServerHandlers_autogen.cpp', wire_params))
renders.append(FileRender('dawn_wire/server/ServerPrototypes.inl', 'dawn_wire/server/ServerPrototypes_autogen.inl', wire_params))

View File

@ -17,7 +17,7 @@
namespace dawn_wire { namespace server {
{% for type in by_category["object"] if type.is_builder%}
void Server::Forward{{type.name.CamelCase()}}ToClient(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) {
void Server::Forward{{type.name.CamelCase()}}(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) {
auto server = reinterpret_cast<Server*>(static_cast<uintptr_t>(userdata1));
uint32_t id = userdata2 & 0xFFFFFFFFu;
uint32_t serial = userdata2 >> uint64_t(32);

View File

@ -0,0 +1,97 @@
//* Copyright 2019 The Dawn Authors
//*
//* Licensed under the Apache License, Version 2.0 (the "License");
//* you may not use this file except in compliance with the License.
//* You may obtain a copy of the License at
//*
//* http://www.apache.org/licenses/LICENSE-2.0
//*
//* Unless required by applicable law or agreed to in writing, software
//* distributed under the License is distributed on an "AS IS" BASIS,
//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//* See the License for the specific language governing permissions and
//* limitations under the License.
#include "common/Assert.h"
#include "dawn_wire/server/Server.h"
namespace dawn_wire { namespace server {
//* Implementation of the command doers
{% for command in cmd_records["command"] %}
{% set type = command.derived_object %}
{% set method = command.derived_method %}
{% set is_method = method is not none %}
{% set Suffix = command.name.CamelCase() %}
{% if Suffix not in client_side_commands %}
{% if is_method and Suffix not in server_handwritten_commands %}
bool Server::Do{{Suffix}}(
{%- for member in command.members -%}
{%- if member.is_return_value -%}
{%- if member.handle_type -%}
{{as_cType(member.handle_type.name)}}* {{as_varName(member.name)}}
{%- else -%}
{{as_cType(member.type.name)}}* {{as_varName(member.name)}}
{%- endif -%}
{%- else -%}
{{as_annotated_cType(member)}}
{%- endif -%}
{%- if not loop.last -%}, {% endif %}
{%- endfor -%}
) {
{% set ret = command.members|selectattr("is_return_value")|list %}
//* If there is a return value, assign it.
{% if ret|length == 1 %}
*{{as_varName(ret[0].name)}} =
{% else %}
//* Only one member should be a return value.
{{ assert(ret|length == 0) }}
{% endif %}
mProcs.{{as_varName(type.name, method.name)}}(
{%- for member in command.members if not member.is_return_value -%}
{{as_varName(member.name)}}
{%- if not loop.last -%}, {% endif %}
{%- endfor -%}
);
return true;
}
{% endif %}
{% endif %}
{% endfor %}
bool Server::DoDestroyObject(ObjectType objectType, ObjectId objectId) {
//* ID 0 are reserved for nullptr and cannot be destroyed.
if (objectId == 0) {
return false;
}
switch(objectType) {
{% for type in by_category["object"] %}
case ObjectType::{{type.name.CamelCase()}}: {
{% if type.name.CamelCase() == "Device" %}
//* Freeing the device has to be done out of band.
return false;
{% else %}
auto* data = {{type.name.CamelCase()}}Objects().Get(objectId);
if (data == nullptr) {
return false;
}
{% if type.name.CamelCase() in server_reverse_lookup_objects %}
{{type.name.CamelCase()}}ObjectIdTable().Remove(data->handle);
{% endif %}
if (data->handle != nullptr) {
mProcs.{{as_varName(type.name, Name("release"))}}(data->handle);
}
{{type.name.CamelCase()}}Objects().Free(objectId);
return true;
{% endif %}
}
{% endfor %}
default:
return false;
}
return true;
}
}} // namespace dawn_wire::server

View File

@ -16,144 +16,120 @@
#include "dawn_wire/server/Server.h"
namespace dawn_wire { namespace server {
{% for type in by_category["object"] %}
{% for method in type.methods %}
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
{% if Suffix not in client_side_commands %}
//* The generic command handlers
{% for command in cmd_records["command"] %}
{% set type = command.derived_object %}
{% set method = command.derived_method %}
{% set is_method = method != None %}
{% set returns = is_method and method.return_type.name.canonical_case() != "void" %}
bool Server::Handle{{Suffix}}(const char** commands, size_t* size) {
{{Suffix}}Cmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator, *this);
{% set Suffix = command.name.CamelCase() %}
{% if Suffix not in client_side_commands %}
//* The generic command handlers
bool Server::Handle{{Suffix}}(const char** commands, size_t* size) {
{{Suffix}}Cmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator
{%- if command.has_dawn_object -%}
, *this
{%- endif -%}
);
if (deserializeResult == DeserializeResult::FatalError) {
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
{% if Suffix in server_custom_pre_handler_commands %}
if (!PreHandle{{Suffix}}(cmd)) {
return false;
}
{% endif %}
{% if Suffix in server_custom_pre_handler_commands %}
if (!PreHandle{{Suffix}}(cmd)) {
return false;
}
{% endif %}
{% if is_method %}
//* Unpack 'self'
auto* selfData = {{type.name.CamelCase()}}Objects().Get(cmd.selfId);
ASSERT(selfData != nullptr);
{% endif %}
//* In all cases allocate the object data as it will be refered-to by the client.
{% set return_type = method.return_type %}
{% set returns = return_type.name.canonical_case() != "void" %}
{% if returns %}
{% set Type = method.return_type.name.CamelCase() %}
auto* resultData = {{Type}}Objects().Allocate(cmd.result.id);
if (resultData == nullptr) {
return false;
}
resultData->serial = cmd.result.serial;
//* Allocate any result objects
{%- for member in command.members if member.is_return_value -%}
{{ assert(member.handle_type) }}
{% set Type = member.handle_type.name.CamelCase() %}
{% set name = as_varName(member.name) %}
{% if type.is_builder %}
selfData->builtObject = cmd.result;
{% endif %}
{% endif %}
//* After the data is allocated, apply the argument error propagation mechanism
if (deserializeResult == DeserializeResult::ErrorObject) {
{% if type.is_builder %}
selfData->valid = false;
//* If we are in GetResult, fake an error callback
{% if returns %}
On{{type.name.CamelCase()}}Error(DAWN_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd.selfId, selfData->serial);
{% endif %}
{% endif %}
return true;
auto* {{name}}Data = {{Type}}Objects().Allocate(cmd.{{name}}.id);
if ({{name}}Data == nullptr) {
return false;
}
{{name}}Data->serial = cmd.{{name}}.serial;
{% if returns %}
auto result ={{" "}}
{%- endif %}
mProcs.{{as_varName(type.name, method.name)}}(cmd.self
{%- for arg in method.arguments -%}
, cmd.{{as_varName(arg.name)}}
{%- endfor -%}
);
{% if Suffix in server_custom_post_handler_commands %}
if (!PostHandle{{Suffix}}(cmd)) {
return false;
}
{% if type.is_builder %}
selfData->builtObject = cmd.{{name}};
{% endif %}
{% endfor %}
{% if returns %}
resultData->handle = result;
resultData->valid = result != nullptr;
{% if return_type.name.CamelCase() in server_reverse_lookup_objects %}
//* For created objects, store a mapping from them back to their client IDs
if (result) {
{{return_type.name.CamelCase()}}ObjectIdTable().Store(result, cmd.result.id);
}
{% endif %}
//* builders remember the ID of the object they built so that they can send it
//* in the callback to the client.
{% if return_type.is_builder %}
if (result != nullptr) {
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.result.id;
mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2);
}
//* After the data is allocated, apply the argument error propagation mechanism
if (deserializeResult == DeserializeResult::ErrorObject) {
{% if type.is_builder %}
selfData->valid = false;
//* If we are in GetResult, fake an error callback
{% if returns %}
On{{type.name.CamelCase()}}Error(DAWN_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd.selfId, selfData->serial);
{% endif %}
{% endif %}
return true;
}
{% endif %}
{% endfor %}
{% endfor %}
bool Server::HandleDestroyObject(const char** commands, size_t* size) {
DestroyObjectCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
//* Do command
bool success = Do{{Suffix}}(
{%- for member in command.members -%}
{%- if member.is_return_value -%}
{%- if member.handle_type -%}
&{{as_varName(member.name)}}Data->handle //* Pass the handle of the output object to be written by the doer
{%- else -%}
&cmd.{{as_varName(member.name)}}
{%- endif -%}
{%- else -%}
cmd.{{as_varName(member.name)}}
{%- endif -%}
{%- if not loop.last -%}, {% endif %}
{%- endfor -%}
);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
//* Mark output object handles as valid/invalid
{%- for member in command.members if member.is_return_value and member.handle_type -%}
{% set name = as_varName(member.name) %}
{{name}}Data->valid = {{name}}Data->handle != nullptr;
{% endfor %}
ObjectId objectId = cmd.objectId;
//* ID 0 are reserved for nullptr and cannot be destroyed.
if (objectId == 0) {
return false;
}
switch (cmd.objectType) {
{% for type in by_category["object"] %}
{% set ObjectType = type.name.CamelCase() %}
case ObjectType::{{ObjectType}}: {
{% if ObjectType == "Device" %}
//* Freeing the device has to be done out of band.
return false;
{% else %}
auto* data = {{type.name.CamelCase()}}Objects().Get(objectId);
if (data == nullptr) {
return false;
}
{% if type.name.CamelCase() in server_reverse_lookup_objects %}
{{type.name.CamelCase()}}ObjectIdTable().Remove(data->handle);
{% endif %}
if (data->handle != nullptr) {
mProcs.{{as_varName(type.name, Name("release"))}}(data->handle);
}
{{type.name.CamelCase()}}Objects().Free(objectId);
return true;
{% endif %}
if (!success) {
return false;
}
{% endfor %}
default:
UNREACHABLE();
}
}
{%- for member in command.members if member.is_return_value and member.handle_type -%}
{% set Type = member.handle_type.name.CamelCase() %}
{% set name = as_varName(member.name) %}
{% if Type in server_reverse_lookup_objects %}
//* For created objects, store a mapping from them back to their client IDs
if ({{name}}Data->valid) {
{{Type}}ObjectIdTable().Store({{name}}Data->handle, cmd.{{name}}.id);
}
{% endif %}
//* builders remember the ID of the object they built so that they can send it
//* in the callback to the client.
{% if member.handle_type.is_builder %}
if ({{name}}Data->valid) {
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
uint64_t userdata2 = (uint64_t({{name}}Data->serial) << uint64_t(32)) + cmd.{{name}}.id;
mProcs.{{as_varName(member.handle_type.name, Name("set error callback"))}}({{name}}Data->handle, Forward{{Type}}, userdata1, userdata2);
}
{% endif %}
{% endfor %}
return true;
}
{% endif %}
{% endfor %}
const char* Server::HandleCommands(const char* commands, size_t size) {
mProcs.deviceTick(DeviceObjects().Get(1)->handle);

View File

@ -14,7 +14,7 @@
// Forwarding callbacks
{% for type in by_category["object"] if type.is_builder%}
static void Forward{{type.name.CamelCase()}}ToClient(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2);
static void Forward{{type.name.CamelCase()}}(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2);
{% endfor %}
// Error callbacks
@ -23,12 +23,27 @@
void On{{Type}}Error(dawnBuilderErrorStatus status, const char* message, uint32_t id, uint32_t serial);
{% endfor %}
// Command handlers
{% for type in by_category["object"] %}
{% for method in type.methods %}
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
{% if Suffix not in client_side_commands %}
bool Handle{{Suffix}}(const char** commands, size_t* size);
{% endif %}
{% endfor %}
// Command handlers & doers
{% for command in cmd_records["command"] if command.name.CamelCase() not in client_side_commands %}
{% set Suffix = command.name.CamelCase() %}
bool Handle{{Suffix}}(const char** commands, size_t* size);
bool Do{{Suffix}}(
{%- for member in command.members -%}
{%- if member.is_return_value -%}
{%- if member.handle_type -%}
{{as_cType(member.handle_type.name)}}* {{as_varName(member.name)}}
{%- else -%}
{{as_cType(member.type.name)}}* {{as_varName(member.name)}}
{%- endif -%}
{%- else -%}
{{as_annotated_cType(member)}}
{%- endif -%}
{%- if not loop.last -%}, {% endif %}
{%- endfor -%}
);
{% endfor %}
{% for CommandName in server_custom_pre_handler_commands %}
bool PreHandle{{CommandName}}(const {{CommandName}}Cmd& cmd);
{% endfor %}

View File

@ -24,7 +24,7 @@ namespace dawn_wire { namespace server {
deviceData->valid = true;
auto userdata = static_cast<dawnCallbackUserdata>(reinterpret_cast<intptr_t>(this));
procs.deviceSetErrorCallback(device, ForwardDeviceErrorToServer, userdata);
procs.deviceSetErrorCallback(device, ForwardDeviceError, userdata);
}
Server::~Server() {

View File

@ -46,7 +46,7 @@ namespace dawn_wire { namespace server {
void* GetCmdSpace(size_t size);
// Forwarding callbacks
static void ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata);
static void ForwardDeviceError(const char* message, dawnCallbackUserdata userdata);
static void ForwardBufferMapReadAsync(dawnBufferMapAsyncStatus status,
const void* ptr,
dawnCallbackUserdata userdata);
@ -66,13 +66,6 @@ namespace dawn_wire { namespace server {
MapUserdata* userdata);
void OnFenceCompletedValueUpdated(FenceCompletionUserdata* userdata);
// Command handlers
bool PreHandleBufferUnmap(const BufferUnmapCmd& cmd);
bool PostHandleQueueSignal(const QueueSignalCmd& cmd);
bool HandleBufferMapAsync(const char** commands, size_t* size);
bool HandleBufferUpdateMappedData(const char** commands, size_t* size);
bool HandleDestroyObject(const char** commands, size_t* size);
#include "dawn_wire/server/ServerPrototypes_autogen.inl"
CommandSerializer* mSerializer = nullptr;

View File

@ -21,28 +21,20 @@ namespace dawn_wire { namespace server {
bool Server::PreHandleBufferUnmap(const BufferUnmapCmd& cmd) {
auto* selfData = BufferObjects().Get(cmd.selfId);
ASSERT(selfData != nullptr);
DAWN_ASSERT(selfData != nullptr);
selfData->mappedData = nullptr;
return true;
}
bool Server::HandleBufferMapAsync(const char** commands, size_t* size) {
bool Server::DoBufferMapAsync(ObjectId bufferId,
uint32_t requestSerial,
uint32_t start,
uint32_t size,
bool isWrite) {
// These requests are just forwarded to the buffer, with userdata containing what the
// client will require in the return command.
BufferMapAsyncCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
ObjectId bufferId = cmd.bufferId;
uint32_t requestSerial = cmd.requestSerial;
uint32_t requestSize = cmd.size;
uint32_t requestStart = cmd.start;
bool isWrite = cmd.isWrite;
// The null object isn't valid as `self`
if (bufferId == 0) {
@ -58,7 +50,7 @@ namespace dawn_wire { namespace server {
data->server = this;
data->buffer = ObjectHandle{bufferId, buffer->serial};
data->requestSerial = requestSerial;
data->size = requestSize;
data->size = size;
data->isWrite = isWrite;
auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
@ -74,27 +66,17 @@ namespace dawn_wire { namespace server {
}
if (isWrite) {
mProcs.bufferMapWriteAsync(buffer->handle, requestStart, requestSize,
ForwardBufferMapWriteAsync, userdata);
mProcs.bufferMapWriteAsync(buffer->handle, start, size, ForwardBufferMapWriteAsync,
userdata);
} else {
mProcs.bufferMapReadAsync(buffer->handle, requestStart, requestSize,
ForwardBufferMapReadAsync, userdata);
mProcs.bufferMapReadAsync(buffer->handle, start, size, ForwardBufferMapReadAsync,
userdata);
}
return true;
}
bool Server::HandleBufferUpdateMappedData(const char** commands, size_t* size) {
BufferUpdateMappedDataCmd cmd;
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator);
if (deserializeResult == DeserializeResult::FatalError) {
return false;
}
ObjectId bufferId = cmd.bufferId;
size_t dataLength = cmd.dataLength;
bool Server::DoBufferUpdateMappedData(ObjectId bufferId, uint32_t count, const uint8_t* data) {
// The null object isn't valid as `self`
if (bufferId == 0) {
return false;
@ -102,13 +84,15 @@ namespace dawn_wire { namespace server {
auto* buffer = BufferObjects().Get(bufferId);
if (buffer == nullptr || !buffer->valid || buffer->mappedData == nullptr ||
buffer->mappedDataSize != dataLength) {
buffer->mappedDataSize != count) {
return false;
}
DAWN_ASSERT(cmd.data != nullptr);
if (data == nullptr) {
return false;
}
memcpy(buffer->mappedData, cmd.data, dataLength);
memcpy(buffer->mappedData, data, count);
return true;
}

View File

@ -16,7 +16,7 @@
namespace dawn_wire { namespace server {
void Server::ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata) {
void Server::ForwardDeviceError(const char* message, dawnCallbackUserdata userdata) {
auto server = reinterpret_cast<Server*>(static_cast<intptr_t>(userdata));
server->OnDeviceError(message);
}

View File

@ -17,11 +17,14 @@
namespace dawn_wire { namespace server {
bool Server::PostHandleQueueSignal(const QueueSignalCmd& cmd) {
if (cmd.fence == nullptr) {
bool Server::DoQueueSignal(dawnQueue cSelf, dawnFence cFence, uint64_t signalValue) {
if (cFence == nullptr) {
return false;
}
ObjectId fenceId = FenceObjectIdTable().Get(cmd.fence);
mProcs.queueSignal(cSelf, cFence, signalValue);
ObjectId fenceId = FenceObjectIdTable().Get(cFence);
ASSERT(fenceId != 0);
auto* fence = FenceObjects().Get(fenceId);
ASSERT(fence != nullptr);
@ -29,10 +32,10 @@ namespace dawn_wire { namespace server {
auto* data = new FenceCompletionUserdata;
data->server = this;
data->fence = ObjectHandle{fenceId, fence->serial};
data->value = cmd.signalValue;
data->value = signalValue;
auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
mProcs.fenceOnCompletion(cmd.fence, cmd.signalValue, ForwardFenceCompletedValue, userdata);
mProcs.fenceOnCompletion(cFence, signalValue, ForwardFenceCompletedValue, userdata);
return true;
}