Add empty implementations of Push/PopErrorScope

This adds Push/PopErrorScope to the API with empty implementations which
just call the error callback. Also adds unittests that the wire callbacks
return as expected.

Bug: dawn:153
Change-Id: I63826360e39fbac4c9855d3d55a05b5ca26db450
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10543
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
Austin Eng 2019-09-04 22:54:03 +00:00 committed by Commit Bot service account
parent f5c44772a6
commit 45238d775a
13 changed files with 350 additions and 0 deletions

View File

@ -527,6 +527,20 @@
{"name": "callback", "type": "error callback"},
{"name": "userdata", "type": "void", "annotation": "*"}
]
},
{
"name": "push error scope",
"args": [
{"name": "filter", "type": "error filter"}
]
},
{
"name": "pop error scope",
"returns": "bool",
"args": [
{"name": "callback", "type": "error callback"},
{"name": "userdata", "type": "void", "annotation": "*"}
]
}
]
},
@ -546,6 +560,14 @@
"error callback": {
"category": "natively defined"
},
"error filter": {
"category": "enum",
"values": [
{"value": 0, "name": "none"},
{"value": 1, "name": "validation"},
{"value": 2, "name": "out of memory"}
]
},
"error type": {
"category": "enum",
"values": [

View File

@ -48,6 +48,10 @@
{ "name": "handle create info length", "type": "uint64_t" },
{ "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
],
"device pop error scope": [
{ "name": "device", "type": "device" },
{ "name": "request serial", "type": "uint64_t" }
],
"destroy object": [
{ "name": "object type", "type": "ObjectType" },
{ "name": "object id", "type": "ObjectId" }
@ -70,6 +74,11 @@
{ "name": "type", "type": "error type"},
{ "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
],
"device pop error scope callback": [
{ "name": "request serial", "type": "uint64_t" },
{ "name": "type", "type": "error type" },
{ "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
],
"fence update completed value": [
{ "name": "fence", "type": "ObjectHandle", "handle_type": "fence" },
{ "name": "value", "type": "uint64_t" }
@ -89,6 +98,8 @@
"DeviceCreateBuffer",
"DeviceCreateBufferMapped",
"DeviceCreateBufferMappedAsync",
"DevicePushErrorScope",
"DevicePopErrorScope",
"QueueCreateFence",
"FenceGetCompletedValue",
"QueueSignal"

View File

@ -60,6 +60,10 @@ void ProcTableAsClass::DeviceSetUncapturedErrorCallback(DawnDevice self,
OnDeviceSetUncapturedErrorCallback(self, callback, userdata);
}
bool ProcTableAsClass::DevicePopErrorScope(DawnDevice self, DawnErrorCallback callback, void* userdata) {
return OnDevicePopErrorScopeCallback(self, callback, userdata);
}
void ProcTableAsClass::DeviceCreateBufferMappedAsync(DawnDevice self,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,

View File

@ -54,6 +54,7 @@ class ProcTableAsClass {
void DeviceSetUncapturedErrorCallback(DawnDevice self,
DawnErrorCallback callback,
void* userdata);
bool DevicePopErrorScope(DawnDevice self, DawnErrorCallback callback, void* userdata);
void DeviceCreateBufferMappedAsync(DawnDevice self,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,
@ -73,6 +74,9 @@ class ProcTableAsClass {
virtual void OnDeviceSetUncapturedErrorCallback(DawnDevice device,
DawnErrorCallback callback,
void* userdata) = 0;
virtual bool OnDevicePopErrorScopeCallback(DawnDevice device,
DawnErrorCallback callback,
void* userdata) = 0;
virtual void OnDeviceCreateBufferMappedAsyncCallback(DawnDevice self,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,
@ -134,6 +138,7 @@ class MockProcTable : public ProcTableAsClass {
{% endfor %}
MOCK_METHOD3(OnDeviceSetUncapturedErrorCallback, void(DawnDevice device, DawnErrorCallback callback, void* userdata));
MOCK_METHOD3(OnDevicePopErrorScopeCallback, bool(DawnDevice device, DawnErrorCallback callback, void* userdata));
MOCK_METHOD4(OnDeviceCreateBufferMappedAsyncCallback, void(DawnDevice device, const DawnBufferDescriptor* descriptor, DawnBufferCreateMappedCallback callback, void* userdata));
MOCK_METHOD3(OnBufferMapReadAsyncCallback, void(DawnBuffer buffer, DawnBufferMapReadCallback callback, void* userdata));
MOCK_METHOD3(OnBufferMapWriteAsyncCallback, void(DawnBuffer buffer, DawnBufferMapWriteCallback callback, void* userdata));

View File

@ -99,6 +99,17 @@ namespace dawn_native {
mErrorUserdata = userdata;
}
void DeviceBase::PushErrorScope(dawn::ErrorFilter filter) {
// TODO(crbug.com/dawn/153): Implement error scopes.
HandleError(dawn::ErrorType::Validation, "Error scopes not implemented");
}
bool DeviceBase::PopErrorScope(dawn::ErrorCallback callback, void* userdata) {
// TODO(crbug.com/dawn/153): Implement error scopes.
HandleError(dawn::ErrorType::Validation, "Error scopes not implemented");
return false;
}
MaybeError DeviceBase::ValidateObject(const ObjectBase* object) const {
if (DAWN_UNLIKELY(object->GetDevice() != this)) {
return DAWN_VALIDATION_ERROR("Object from a different device.");

View File

@ -149,6 +149,9 @@ namespace dawn_native {
void Tick();
void SetUncapturedErrorCallback(dawn::ErrorCallback callback, void* userdata);
void PushErrorScope(dawn::ErrorFilter filter);
bool PopErrorScope(dawn::ErrorCallback callback, void* userdata);
void Reference();
void Release();

View File

@ -264,6 +264,16 @@ namespace dawn_wire { namespace client {
writeHandle->SerializeCreate(allocatedBuffer + commandSize);
}
void ClientDevicePushErrorScope(DawnDevice cDevice, DawnErrorFilter filter) {
Device* device = reinterpret_cast<Device*>(cDevice);
device->PushErrorScope(filter);
}
bool ClientDevicePopErrorScope(DawnDevice cDevice, DawnErrorCallback callback, void* userdata) {
Device* device = reinterpret_cast<Device*>(cDevice);
return device->RequestPopErrorScope(callback, userdata);
}
uint64_t ClientFenceGetCompletedValue(DawnFence cSelf) {
auto fence = reinterpret_cast<Fence*>(cSelf);
return fence->completedValue;

View File

@ -33,6 +33,12 @@ namespace dawn_wire { namespace client {
return true;
}
bool Client::DoDevicePopErrorScopeCallback(uint64_t requestSerial,
DawnErrorType errorType,
const char* message) {
return mDevice->PopErrorScope(requestSerial, errorType, message);
}
bool Client::DoBufferMapReadAsyncCallback(Buffer* buffer,
uint32_t requestSerial,
uint32_t status,

View File

@ -14,6 +14,10 @@
#include "dawn_wire/client/Device.h"
#include "common/Assert.h"
#include "dawn_wire/WireCmd_autogen.h"
#include "dawn_wire/client/Client.h"
namespace dawn_wire { namespace client {
Device::Device(Client* client, uint32_t refcount, uint32_t id)
@ -21,6 +25,13 @@ namespace dawn_wire { namespace client {
this->device = this;
}
Device::~Device() {
auto errorScopes = std::move(mErrorScopes);
for (const auto& it : errorScopes) {
it.second.callback(DAWN_ERROR_TYPE_UNKNOWN, "Device destroyed", it.second.userdata);
}
}
Client* Device::GetClient() {
return mClient;
}
@ -36,4 +47,64 @@ namespace dawn_wire { namespace client {
mErrorUserdata = errorUserdata;
}
void Device::PushErrorScope(DawnErrorFilter filter) {
mErrorScopeStackSize++;
DevicePushErrorScopeCmd cmd;
cmd.self = reinterpret_cast<DawnDevice>(this);
cmd.filter = filter;
Client* wireClient = GetClient();
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(wireClient->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer, *wireClient);
}
bool Device::RequestPopErrorScope(DawnErrorCallback callback, void* userdata) {
if (mErrorScopeStackSize == 0) {
return false;
}
mErrorScopeStackSize--;
uint64_t serial = mErrorScopeRequestSerial++;
ASSERT(mErrorScopes.find(serial) == mErrorScopes.end());
mErrorScopes[serial] = {callback, userdata};
DevicePopErrorScopeCmd cmd;
cmd.device = reinterpret_cast<DawnDevice>(this);
cmd.requestSerial = serial;
Client* wireClient = GetClient();
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(wireClient->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer, *wireClient);
return true;
}
bool Device::PopErrorScope(uint64_t requestSerial, DawnErrorType type, const char* message) {
switch (type) {
case DAWN_ERROR_TYPE_NO_ERROR:
case DAWN_ERROR_TYPE_VALIDATION:
case DAWN_ERROR_TYPE_OUT_OF_MEMORY:
case DAWN_ERROR_TYPE_UNKNOWN:
case DAWN_ERROR_TYPE_DEVICE_LOST:
break;
default:
return false;
}
auto requestIt = mErrorScopes.find(requestSerial);
if (requestIt == mErrorScopes.end()) {
return false;
}
ErrorScopeData request = std::move(requestIt->second);
mErrorScopes.erase(requestIt);
request.callback(type, message, request.userdata);
return true;
}
}} // namespace dawn_wire::client

View File

@ -19,6 +19,8 @@
#include "dawn_wire/client/ObjectBase.h"
#include <map>
namespace dawn_wire { namespace client {
class Client;
@ -26,12 +28,25 @@ namespace dawn_wire { namespace client {
class Device : public ObjectBase {
public:
Device(Client* client, uint32_t refcount, uint32_t id);
~Device();
Client* GetClient();
void HandleError(DawnErrorType errorType, const char* message);
void SetUncapturedErrorCallback(DawnErrorCallback errorCallback, void* errorUserdata);
void PushErrorScope(DawnErrorFilter filter);
bool RequestPopErrorScope(DawnErrorCallback callback, void* userdata);
bool PopErrorScope(uint64_t requestSerial, DawnErrorType type, const char* message);
private:
struct ErrorScopeData {
DawnErrorCallback callback = nullptr;
void* userdata = nullptr;
};
std::map<uint64_t, ErrorScopeData> mErrorScopes;
uint64_t mErrorScopeRequestSerial = 0;
uint64_t mErrorScopeStackSize = 0;
Client* mClient = nullptr;
DawnErrorCallback mErrorCallback = nullptr;
void* mErrorUserdata;

View File

@ -32,6 +32,13 @@ namespace dawn_wire { namespace server {
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
};
struct ErrorScopeUserdata {
Server* server;
// TODO(enga): ObjectHandle device;
// when the wire supports multiple devices.
uint32_t requestSerial;
};
struct FenceCompletionUserdata {
Server* server;
ObjectHandle fence;
@ -55,6 +62,7 @@ namespace dawn_wire { namespace server {
// Forwarding callbacks
static void ForwardUncapturedError(DawnErrorType type, const char* message, void* userdata);
static void ForwardPopErrorScope(DawnErrorType type, const char* message, void* userdata);
static void ForwardBufferMapReadAsync(DawnBufferMapAsyncStatus status,
const void* ptr,
uint64_t dataLength,
@ -67,6 +75,9 @@ namespace dawn_wire { namespace server {
// Error callbacks
void OnUncapturedError(DawnErrorType type, const char* message);
void OnDevicePopErrorScope(DawnErrorType type,
const char* message,
ErrorScopeUserdata* userdata);
void OnBufferMapReadAsyncCallback(DawnBufferMapAsyncStatus status,
const void* ptr,
uint64_t dataLength,

View File

@ -31,4 +31,33 @@ namespace dawn_wire { namespace server {
cmd.Serialize(allocatedBuffer);
}
bool Server::DoDevicePopErrorScope(DawnDevice cDevice, uint64_t requestSerial) {
ErrorScopeUserdata* userdata = new ErrorScopeUserdata;
userdata->server = this;
userdata->requestSerial = requestSerial;
return mProcs.devicePopErrorScope(cDevice, ForwardPopErrorScope, userdata);
}
// static
void Server::ForwardPopErrorScope(DawnErrorType type, const char* message, void* userdata) {
auto* data = reinterpret_cast<ErrorScopeUserdata*>(userdata);
data->server->OnDevicePopErrorScope(type, message, data);
}
void Server::OnDevicePopErrorScope(DawnErrorType type,
const char* message,
ErrorScopeUserdata* userdata) {
std::unique_ptr<ErrorScopeUserdata> data{userdata};
ReturnDevicePopErrorScopeCallbackCmd cmd;
cmd.requestSerial = data->requestSerial;
cmd.type = type;
cmd.message = message;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
}
}} // namespace dawn_wire::server

View File

@ -30,6 +30,16 @@ namespace {
mockDeviceErrorCallback->Call(type, message, userdata);
}
class MockDevicePopErrorScopeCallback {
public:
MOCK_METHOD3(Call, void(DawnErrorType type, const char* message, void* userdata));
};
std::unique_ptr<StrictMock<MockDevicePopErrorScopeCallback>> mockDevicePopErrorScopeCallback;
void ToMockDevicePopErrorScopeCallback(DawnErrorType type, const char* message, void* userdata) {
mockDevicePopErrorScopeCallback->Call(type, message, userdata);
}
} // anonymous namespace
class WireErrorCallbackTests : public WireTest {
@ -42,18 +52,21 @@ class WireErrorCallbackTests : public WireTest {
WireTest::SetUp();
mockDeviceErrorCallback = std::make_unique<StrictMock<MockDeviceErrorCallback>>();
mockDevicePopErrorScopeCallback = std::make_unique<StrictMock<MockDevicePopErrorScopeCallback>>();
}
void TearDown() override {
WireTest::TearDown();
mockDeviceErrorCallback = nullptr;
mockDevicePopErrorScopeCallback = nullptr;
}
void FlushServer() {
WireTest::FlushServer();
Mock::VerifyAndClearExpectations(&mockDeviceErrorCallback);
Mock::VerifyAndClearExpectations(&mockDevicePopErrorScopeCallback);
}
};
@ -72,3 +85,142 @@ TEST_F(WireErrorCallbackTests, DeviceErrorCallback) {
FlushServer();
}
// Test the return wire for error scopes.
TEST_F(WireErrorCallbackTests, PushPopErrorScopeCallback) {
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
EXPECT_CALL(api, DevicePushErrorScope(apiDevice, DAWN_ERROR_FILTER_VALIDATION)).Times(1);
FlushClient();
dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this);
DawnErrorCallback callback;
void* userdata;
EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _))
.WillOnce(DoAll(SaveArg<1>(&callback), SaveArg<2>(&userdata), Return(true)));
FlushClient();
callback(DAWN_ERROR_TYPE_VALIDATION, "Some error message", userdata);
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_VALIDATION, StrEq("Some error message"), this)).Times(1);
FlushServer();
}
// Test the return wire for error scopes when callbacks return in a various orders.
TEST_F(WireErrorCallbackTests, PopErrorScopeCallbackOrdering) {
// Two error scopes are popped, and the first one returns first.
{
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
EXPECT_CALL(api, DevicePushErrorScope(apiDevice, DAWN_ERROR_FILTER_VALIDATION)).Times(2);
FlushClient();
dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this);
dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1);
DawnErrorCallback callback1;
DawnErrorCallback callback2;
void* userdata1;
void* userdata2;
EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _))
.WillOnce(DoAll(SaveArg<1>(&callback1), SaveArg<2>(&userdata1), Return(true)))
.WillOnce(DoAll(SaveArg<1>(&callback2), SaveArg<2>(&userdata2), Return(true)));
FlushClient();
callback1(DAWN_ERROR_TYPE_VALIDATION, "First error message", userdata1);
EXPECT_CALL(*mockDevicePopErrorScopeCallback,
Call(DAWN_ERROR_TYPE_VALIDATION,
StrEq("First error message"), this)).Times(1);
FlushServer();
callback2(DAWN_ERROR_TYPE_VALIDATION, "Second error message", userdata2);
EXPECT_CALL(*mockDevicePopErrorScopeCallback,
Call(DAWN_ERROR_TYPE_VALIDATION,
StrEq("Second error message"), this + 1)).Times(1);
FlushServer();
}
// Two error scopes are popped, and the second one returns first.
{
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
EXPECT_CALL(api, DevicePushErrorScope(apiDevice, DAWN_ERROR_FILTER_VALIDATION)).Times(2);
FlushClient();
dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this);
dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1);
DawnErrorCallback callback1;
DawnErrorCallback callback2;
void* userdata1;
void* userdata2;
EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _))
.WillOnce(DoAll(SaveArg<1>(&callback1), SaveArg<2>(&userdata1), Return(true)))
.WillOnce(DoAll(SaveArg<1>(&callback2), SaveArg<2>(&userdata2), Return(true)));
FlushClient();
callback2(DAWN_ERROR_TYPE_VALIDATION, "Second error message", userdata2);
EXPECT_CALL(*mockDevicePopErrorScopeCallback,
Call(DAWN_ERROR_TYPE_VALIDATION,
StrEq("Second error message"), this + 1)).Times(1);
FlushServer();
callback1(DAWN_ERROR_TYPE_VALIDATION, "First error message", userdata1);
EXPECT_CALL(*mockDevicePopErrorScopeCallback,
Call(DAWN_ERROR_TYPE_VALIDATION,
StrEq("First error message"), this)).Times(1);
FlushServer();
}
}
// Test the return wire for error scopes in flight when the device is destroyed.
TEST_F(WireErrorCallbackTests, PopErrorScopeDeviceDestroyed) {
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
EXPECT_CALL(api, DevicePushErrorScope(apiDevice, DAWN_ERROR_FILTER_VALIDATION)).Times(1);
FlushClient();
EXPECT_TRUE(dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _))
.WillOnce(Return(true));
FlushClient();
// Incomplete callback called in Device destructor.
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_UNKNOWN, _, this)).Times(1);
}
// Test that PopErrorScope returns false if there are no error scopes.
TEST_F(WireErrorCallbackTests, PopErrorScopeEmptyStack) {
// Empty stack
{
EXPECT_FALSE(dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
}
// Pop too many times
{
dawnDevicePushErrorScope(device, DAWN_ERROR_FILTER_VALIDATION);
EXPECT_CALL(api, DevicePushErrorScope(apiDevice, DAWN_ERROR_FILTER_VALIDATION)).Times(1);
EXPECT_TRUE(dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
EXPECT_FALSE(dawnDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1));
DawnErrorCallback callback;
void* userdata;
EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _))
.WillOnce(DoAll(SaveArg<1>(&callback), SaveArg<2>(&userdata), Return(true)));
FlushClient();
callback(DAWN_ERROR_TYPE_VALIDATION, "Some error message", userdata);
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_VALIDATION, StrEq("Some error message"), this)).Times(1);
FlushServer();
}
}