Austin Eng b70a5b02e9 Reject all callbacks with DeviceLost on wire client disconnect
When the wire is disconnected, the client will not receive any
messages from the server. We need to manually reject all callbacks.

Bug: dawn:556
Change-Id: Ia03456b3209dbe0e1e54543d344180d11d4c6f1e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/31162
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
2020-11-11 21:01:18 +00:00

314 lines
12 KiB
C++

// 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 "dawn_wire/client/Device.h"
#include "common/Assert.h"
#include "dawn_wire/client/ApiObjects_autogen.h"
#include "dawn_wire/client/Client.h"
#include "dawn_wire/client/ObjectAllocator.h"
namespace dawn_wire { namespace client {
Device::Device(Client* client, uint32_t initialRefcount, uint32_t initialId)
: ObjectBase(this, initialRefcount, initialId), mClient(client) {
// Get the default queue for this device.
auto* allocation = mClient->QueueAllocator().New(this);
mDefaultQueue = allocation->object.get();
DeviceGetDefaultQueueCmd cmd;
cmd.self = ToAPI(this);
cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
mClient->SerializeCommand(cmd);
}
Device::~Device() {
// Fire pending error scopes
auto errorScopes = std::move(mErrorScopes);
for (const auto& it : errorScopes) {
it.second.callback(WGPUErrorType_Unknown, "Device destroyed before callback",
it.second.userdata);
}
auto createReadyPipelineRequests = std::move(mCreateReadyPipelineRequests);
for (const auto& it : createReadyPipelineRequests) {
if (it.second.createReadyComputePipelineCallback != nullptr) {
it.second.createReadyComputePipelineCallback(
WGPUCreateReadyPipelineStatus_DeviceDestroyed, nullptr,
"Device destroyed before callback", it.second.userdata);
} else {
ASSERT(it.second.createReadyRenderPipelineCallback != nullptr);
it.second.createReadyRenderPipelineCallback(
WGPUCreateReadyPipelineStatus_DeviceDestroyed, nullptr,
"Device destroyed before callback", it.second.userdata);
}
}
DestroyAllObjects();
}
void Device::DestroyAllObjects() {
for (auto& objectList : mObjects) {
ObjectType objectType = static_cast<ObjectType>(&objectList - mObjects.begin());
while (!objectList.empty()) {
ObjectBase* object = objectList.head()->value();
DestroyObjectCmd cmd;
cmd.objectType = objectType;
cmd.objectId = object->id;
mClient->SerializeCommand(cmd);
mClient->FreeObject(objectType, object);
}
}
}
Client* Device::GetClient() {
return mClient;
}
void Device::HandleError(WGPUErrorType errorType, const char* message) {
if (mErrorCallback) {
mErrorCallback(errorType, message, mErrorUserdata);
}
}
void Device::HandleDeviceLost(const char* message) {
if (mDeviceLostCallback && !mDidRunLostCallback) {
mDidRunLostCallback = true;
mDeviceLostCallback(message, mDeviceLostUserdata);
}
}
void Device::CancelCallbacksForDisconnect() {
for (auto& it : mCreateReadyPipelineRequests) {
ASSERT((it.second.createReadyComputePipelineCallback != nullptr) ^
(it.second.createReadyRenderPipelineCallback != nullptr));
if (it.second.createReadyRenderPipelineCallback) {
it.second.createReadyRenderPipelineCallback(
WGPUCreateReadyPipelineStatus_DeviceLost, nullptr, "Device lost",
it.second.userdata);
} else {
it.second.createReadyComputePipelineCallback(
WGPUCreateReadyPipelineStatus_DeviceLost, nullptr, "Device lost",
it.second.userdata);
}
}
mCreateReadyPipelineRequests.clear();
for (auto& it : mErrorScopes) {
it.second.callback(WGPUErrorType_DeviceLost, "Device lost", it.second.userdata);
}
mErrorScopes.clear();
for (auto& objectList : mObjects) {
LinkNode<ObjectBase>* object = objectList.head();
while (object != objectList.end()) {
object->value()->CancelCallbacksForDisconnect();
object = object->next();
}
}
}
void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) {
mErrorCallback = errorCallback;
mErrorUserdata = errorUserdata;
}
void Device::SetDeviceLostCallback(WGPUDeviceLostCallback callback, void* userdata) {
mDeviceLostCallback = callback;
mDeviceLostUserdata = userdata;
}
void Device::PushErrorScope(WGPUErrorFilter filter) {
mErrorScopeStackSize++;
DevicePushErrorScopeCmd cmd;
cmd.self = ToAPI(this);
cmd.filter = filter;
mClient->SerializeCommand(cmd);
}
bool Device::PopErrorScope(WGPUErrorCallback 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 = ToAPI(this);
cmd.requestSerial = serial;
mClient->SerializeCommand(cmd);
return true;
}
bool Device::OnPopErrorScopeCallback(uint64_t requestSerial,
WGPUErrorType type,
const char* message) {
switch (type) {
case WGPUErrorType_NoError:
case WGPUErrorType_Validation:
case WGPUErrorType_OutOfMemory:
case WGPUErrorType_Unknown:
case WGPUErrorType_DeviceLost:
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;
}
void Device::InjectError(WGPUErrorType type, const char* message) {
DeviceInjectErrorCmd cmd;
cmd.self = ToAPI(this);
cmd.type = type;
cmd.message = message;
mClient->SerializeCommand(cmd);
}
WGPUBuffer Device::CreateBuffer(const WGPUBufferDescriptor* descriptor) {
return Buffer::Create(this, descriptor);
}
WGPUBuffer Device::CreateErrorBuffer() {
return Buffer::CreateError(this);
}
WGPUQueue Device::GetDefaultQueue() {
mDefaultQueue->refcount++;
return ToAPI(mDefaultQueue);
}
void Device::CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata) {
DeviceCreateReadyComputePipelineCmd cmd;
cmd.device = ToAPI(this);
cmd.descriptor = descriptor;
uint64_t serial = mCreateReadyPipelineRequestSerial++;
ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end());
cmd.requestSerial = serial;
auto* allocation = GetClient()->ComputePipelineAllocator().New(this);
CreateReadyPipelineRequest request = {};
request.createReadyComputePipelineCallback = callback;
request.userdata = userdata;
request.pipelineObjectID = allocation->object->id;
cmd.pipelineObjectHandle = ObjectHandle{allocation->object->id, allocation->generation};
GetClient()->SerializeCommand(cmd);
mCreateReadyPipelineRequests[serial] = std::move(request);
}
bool Device::OnCreateReadyComputePipelineCallback(uint64_t requestSerial,
WGPUCreateReadyPipelineStatus status,
const char* message) {
const auto& requestIt = mCreateReadyPipelineRequests.find(requestSerial);
if (requestIt == mCreateReadyPipelineRequests.end()) {
return false;
}
CreateReadyPipelineRequest request = std::move(requestIt->second);
mCreateReadyPipelineRequests.erase(requestIt);
auto pipelineAllocation =
GetClient()->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
// If the return status is a failure we should give a null pipeline to the callback and
// free the allocation both on the client side and the server side.
if (status != WGPUCreateReadyPipelineStatus_Success) {
GetClient()->ComputePipelineAllocator().Free(pipelineAllocation);
request.createReadyComputePipelineCallback(status, nullptr, message, request.userdata);
return true;
}
WGPUComputePipeline pipeline = reinterpret_cast<WGPUComputePipeline>(pipelineAllocation);
request.createReadyComputePipelineCallback(status, pipeline, message, request.userdata);
return true;
}
void Device::CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor,
WGPUCreateReadyRenderPipelineCallback callback,
void* userdata) {
DeviceCreateReadyRenderPipelineCmd cmd;
cmd.device = ToAPI(this);
cmd.descriptor = descriptor;
uint64_t serial = mCreateReadyPipelineRequestSerial++;
ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end());
cmd.requestSerial = serial;
auto* allocation = GetClient()->RenderPipelineAllocator().New(this);
CreateReadyPipelineRequest request = {};
request.createReadyRenderPipelineCallback = callback;
request.userdata = userdata;
request.pipelineObjectID = allocation->object->id;
cmd.pipelineObjectHandle = ObjectHandle(allocation->object->id, allocation->generation);
GetClient()->SerializeCommand(cmd);
mCreateReadyPipelineRequests[serial] = std::move(request);
}
bool Device::OnCreateReadyRenderPipelineCallback(uint64_t requestSerial,
WGPUCreateReadyPipelineStatus status,
const char* message) {
const auto& requestIt = mCreateReadyPipelineRequests.find(requestSerial);
if (requestIt == mCreateReadyPipelineRequests.end()) {
return false;
}
CreateReadyPipelineRequest request = std::move(requestIt->second);
mCreateReadyPipelineRequests.erase(requestIt);
auto pipelineAllocation =
GetClient()->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
// If the return status is a failure we should give a null pipeline to the callback and
// free the allocation both on the client side and the server side.
if (status != WGPUCreateReadyPipelineStatus_Success) {
GetClient()->RenderPipelineAllocator().Free(pipelineAllocation);
request.createReadyRenderPipelineCallback(status, nullptr, message, request.userdata);
return true;
}
WGPURenderPipeline pipeline = reinterpret_cast<WGPURenderPipeline>(pipelineAllocation);
request.createReadyRenderPipelineCallback(status, pipeline, message, request.userdata);
return true;
}
}} // namespace dawn_wire::client