dawn_wire: Reject new callbacks if the client is disconnected

If the wire client is disconnected, it will not receive any
messages from the server. Reject all callbacks that are created.

Bug: dawn:556
Change-Id: I2eb2c449b1ca6c8ea3e74040ef095abfc46a9061
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/31161
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Austin Eng 2020-11-12 18:03:42 +00:00 committed by Commit Bot service account
parent 01e969da33
commit 7ceffe8511
9 changed files with 116 additions and 0 deletions

View File

@ -124,6 +124,10 @@ namespace dawn_wire { namespace client {
size_t size, size_t size,
WGPUBufferMapCallback callback, WGPUBufferMapCallback callback,
void* userdata) { void* userdata) {
if (device->GetClient()->IsDisconnected()) {
return callback(WGPUBufferMapAsyncStatus_DeviceLost, userdata);
}
// Handle the defaulting of size required by WebGPU. // Handle the defaulting of size required by WebGPU.
if (size == 0 && offset < mSize) { if (size == 0 && offset < mSize) {
size = mSize - offset; size = mSize - offset;

View File

@ -83,6 +83,7 @@ namespace dawn_wire { namespace client {
} }
void Client::Disconnect() { void Client::Disconnect() {
mDisconnected = true;
mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance()); mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance());
if (mDevice != nullptr) { if (mDevice != nullptr) {
mDevice->HandleDeviceLost("GPU connection lost"); mDevice->HandleDeviceLost("GPU connection lost");
@ -94,4 +95,8 @@ namespace dawn_wire { namespace client {
mDevices.Append(device); mDevices.Append(device);
} }
bool Client::IsDisconnected() const {
return mDisconnected;
}
}} // namespace dawn_wire::client }} // namespace dawn_wire::client

View File

@ -60,6 +60,7 @@ namespace dawn_wire { namespace client {
} }
void Disconnect(); void Disconnect();
bool IsDisconnected() const;
void TrackObject(Device* device); void TrackObject(Device* device);
@ -75,6 +76,7 @@ namespace dawn_wire { namespace client {
std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr; std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr;
LinkedList<ObjectBase> mDevices; LinkedList<ObjectBase> mDevices;
bool mDisconnected = false;
}; };
std::unique_ptr<MemoryTransferService> CreateInlineMemoryTransferService(); std::unique_ptr<MemoryTransferService> CreateInlineMemoryTransferService();

View File

@ -147,6 +147,11 @@ namespace dawn_wire { namespace client {
} }
mErrorScopeStackSize--; mErrorScopeStackSize--;
if (GetClient()->IsDisconnected()) {
callback(WGPUErrorType_DeviceLost, "GPU device disconnected", userdata);
return true;
}
uint64_t serial = mErrorScopeRequestSerial++; uint64_t serial = mErrorScopeRequestSerial++;
ASSERT(mErrorScopes.find(serial) == mErrorScopes.end()); ASSERT(mErrorScopes.find(serial) == mErrorScopes.end());
@ -211,6 +216,11 @@ namespace dawn_wire { namespace client {
void Device::CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor, void Device::CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor,
WGPUCreateReadyComputePipelineCallback callback, WGPUCreateReadyComputePipelineCallback callback,
void* userdata) { void* userdata) {
if (device->GetClient()->IsDisconnected()) {
return callback(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr,
"GPU device disconnected", userdata);
}
DeviceCreateReadyComputePipelineCmd cmd; DeviceCreateReadyComputePipelineCmd cmd;
cmd.device = ToAPI(this); cmd.device = ToAPI(this);
cmd.descriptor = descriptor; cmd.descriptor = descriptor;
@ -262,6 +272,10 @@ namespace dawn_wire { namespace client {
void Device::CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor, void Device::CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor,
WGPUCreateReadyRenderPipelineCallback callback, WGPUCreateReadyRenderPipelineCallback callback,
void* userdata) { void* userdata) {
if (GetClient()->IsDisconnected()) {
return callback(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr,
"GPU device disconnected", userdata);
}
DeviceCreateReadyRenderPipelineCmd cmd; DeviceCreateReadyRenderPipelineCmd cmd;
cmd.device = ToAPI(this); cmd.device = ToAPI(this);
cmd.descriptor = descriptor; cmd.descriptor = descriptor;

View File

@ -48,6 +48,10 @@ namespace dawn_wire { namespace client {
void Fence::OnCompletion(uint64_t value, void Fence::OnCompletion(uint64_t value,
WGPUFenceOnCompletionCallback callback, WGPUFenceOnCompletionCallback callback,
void* userdata) { void* userdata) {
if (device->GetClient()->IsDisconnected()) {
return callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
}
uint32_t serial = mOnCompletionRequestSerial++; uint32_t serial = mOnCompletionRequestSerial++;
ASSERT(mOnCompletionRequests.find(serial) == mOnCompletionRequests.end()); ASSERT(mOnCompletionRequests.find(serial) == mOnCompletionRequests.end());

View File

@ -692,3 +692,12 @@ TEST_F(WireBufferMappingTests, MapThenDisconnect) {
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
GetWireClient()->Disconnect(); GetWireClient()->Disconnect();
} }
// Test that registering a callback after wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireBufferMappingTests, MapAfterDisconnect) {
GetWireClient()->Disconnect();
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, this);
}

View File

@ -276,3 +276,55 @@ TEST_F(WireCreateReadyPipelineTest, CreateReadyComputePipelineThenDisconnect) {
.Times(1); .Times(1);
GetWireClient()->Disconnect(); GetWireClient()->Disconnect();
} }
// Test that registering a callback after wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireCreateReadyPipelineTest, CreateReadyRenderPipelineAfterDisconnect) {
WGPUShaderModuleDescriptor vertexDescriptor = {};
WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor);
WGPUShaderModule apiVsModule = api.GetNewShaderModule();
EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
WGPUProgrammableStageDescriptor fragmentStage = {};
fragmentStage.module = vsModule;
fragmentStage.entryPoint = "main";
WGPURenderPipelineDescriptor pipelineDescriptor{};
pipelineDescriptor.vertexStage.module = vsModule;
pipelineDescriptor.vertexStage.entryPoint = "main";
pipelineDescriptor.fragmentStage = &fragmentStage;
FlushClient();
GetWireClient()->Disconnect();
EXPECT_CALL(*mockCreateReadyRenderPipelineCallback,
Call(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr, _, this))
.Times(1);
wgpuDeviceCreateReadyRenderPipeline(device, &pipelineDescriptor,
ToMockCreateReadyRenderPipelineCallback, this);
}
// Test that registering a callback after wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireCreateReadyPipelineTest, CreateReadyComputePipelineAfterDisconnect) {
WGPUShaderModuleDescriptor csDescriptor{};
WGPUShaderModule csModule = wgpuDeviceCreateShaderModule(device, &csDescriptor);
WGPUShaderModule apiCsModule = api.GetNewShaderModule();
EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiCsModule));
WGPUComputePipelineDescriptor descriptor{};
descriptor.computeStage.module = csModule;
descriptor.computeStage.entryPoint = "main";
FlushClient();
GetWireClient()->Disconnect();
EXPECT_CALL(*mockCreateReadyComputePipelineCallback,
Call(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr, _, this))
.Times(1);
wgpuDeviceCreateReadyComputePipeline(device, &descriptor,
ToMockCreateReadyComputePipelineCallback, this);
}

View File

@ -235,6 +235,22 @@ TEST_F(WireErrorCallbackTests, PopErrorScopeThenDisconnect) {
GetWireClient()->Disconnect(); GetWireClient()->Disconnect();
} }
// Test that registering a callback after wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireErrorCallbackTests, PopErrorScopeAfterDisconnect) {
wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1);
FlushClient();
GetWireClient()->Disconnect();
EXPECT_CALL(*mockDevicePopErrorScopeCallback,
Call(WGPUErrorType_DeviceLost, ValidStringMessage(), this))
.Times(1);
EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
}
// Test that PopErrorScope returns false if there are no error scopes. // Test that PopErrorScope returns false if there are no error scopes.
TEST_F(WireErrorCallbackTests, PopErrorScopeEmptyStack) { TEST_F(WireErrorCallbackTests, PopErrorScopeEmptyStack) {
// Empty stack // Empty stack

View File

@ -160,6 +160,16 @@ TEST_F(WireFenceTests, OnCompletionThenDisconnect) {
GetWireClient()->Disconnect(); GetWireClient()->Disconnect();
} }
// Test that registering a callback after wire disconnect calls the callback with
// DeviceLost.
TEST_F(WireFenceTests, OnCompletionAfterDisconnect) {
GetWireClient()->Disconnect();
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_DeviceLost, this))
.Times(1);
wgpuFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, this);
}
// Without any flushes, it is valid to wait on a value less than or equal to // Without any flushes, it is valid to wait on a value less than or equal to
// the last signaled value // the last signaled value
TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) { TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) {