1
0
mirror of https://github.com/apple/foundationdb.git synced 2025-05-28 02:48:09 +08:00
2022-08-01 18:56:50 -07:00

155 lines
5.6 KiB
C++

/*
* OwningResource.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project 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.
*/
#ifndef FLOW_OWNING_REOSURCE_H
#define FLOW_OWNING_REOSURCE_H
#include "flow/FastRef.h"
// Consider the following situation:
//
// 1. An ACTOR A0 allocates an object O
// 2. A0 spawns another ACTOR A1, which depends on O
// 3. A0 triggers A1 and then terminates, destroying O
// 4. Since A1 is triggered by A0 while not knowing A0 is terminated and O is released, it would cause a SEGV error.
//
// In this header file, two classes
//
// * ResourceOwningRef
// * ResourceWeakRef
//
// are provided. The ResourceOwningRef is the reference that "holds" the resource, When it is destructed, the resource
// is also released; while ResourceWeakRef is the reference that "weakly holds" the resource. Before each access, it is
// the user's responsibility to verify if the resource is still available, via the available() method.
//
// With the two classes, the issue above can be solved by:
//
// 1. A0 allocates the object O via ResourceOwningRef
// 2. A0 forwards O to A1, via ResourceWeakRef
// 3. Every time A1 accesses O, it will verify if the resource is still available.
// 4. When A0 terminates, O is released and all ResourceWeakRef available() call will report the resource is not
// available anymore, preventing the SEGV error being raised.
namespace details {
// The class holding the pointer to the resource.
// SOMEDAY: Think using std::unique_ptr
template <typename T>
struct Resource : public ReferenceCounted<Resource<T>>, NonCopyable {
T* resource;
Resource(T* resource_) : resource(resource_) {}
~Resource() { delete resource; }
void reset(T* resource_) {
delete resource;
resource = resource_;
}
};
template <typename T>
class ResourceRef {
protected:
Reference<Resource<T>> resourceRef;
ResourceRef(const Reference<Resource<T>>& ref) : resourceRef(ref) {}
ResourceRef(Reference<Resource<T>>&& ref) : resourceRef(std::move(ref)) {}
ResourceRef& operator=(const Reference<Resource<T>>& ref) {
resourceRef = ref.resourceRef;
return *this;
}
ResourceRef& operator=(Reference<Resource<T>>&& ref) {
resourceRef = std::move(ref);
return *this;
}
virtual ~ResourceRef() {}
public:
// Retrieves the resource as a pointer
T* operator->() const noexcept { return resourceRef->resource; }
// Retrieves the resource as a reference
T& operator*() const {
if (!available()) {
throw internal_error();
} else {
return *(resourceRef->resource);
}
}
// Returns true if the resource is available, i.e. not nullptr
bool available() const noexcept { return resourceRef->resource != nullptr; }
};
} // namespace details
// The class that holds a Reference to the details::Resource which holds the real object. If the instance is destroyed,
// the object is destroyed, too.
template <typename T>
class ResourceOwningRef : public details::ResourceRef<T>, NonCopyable {
template <typename U>
friend class ResourceWeakRef;
template <typename U>
friend class ActorWeakSelfRef;
public:
ResourceOwningRef(T* resource) : details::ResourceRef<T>(makeReference<details::Resource<T>>(resource)) {}
virtual ~ResourceOwningRef() { details::ResourceRef<T>::resourceRef->reset(nullptr); }
};
// The class that weakly holds a Reference to the details::Resource. Destroying the reference will have no impact to the
// real object. On the other hand, each time accessing the object requires a verification that the object is still alive
template <typename T>
class ResourceWeakRef : public details::ResourceRef<T> {
public:
ResourceWeakRef(const ResourceOwningRef<T>& ref) : details::ResourceRef<T>(ref.resourceRef) {}
ResourceWeakRef(const ResourceWeakRef& ref) : details::ResourceRef<T>(ref.resourceRef) {}
};
// A unique reference that takes the ownership of the self object. The self object is widely used as the "global"
// context of each role.
template <typename T>
using ActorOwningSelfRef = ResourceOwningRef<T>;
// A wrapper of ResourceWeakRef, used to forward the widely used `self` pointer from the core ACTOR to other ACTORs. It
// will check the resource before returning it. If the resource is not available, an operation_cancelled error will be
// thrown to terminate the current ACTOR.
template <typename T>
class ActorWeakSelfRef : public ResourceWeakRef<T> {
public:
ActorWeakSelfRef(const ResourceOwningRef<T>& ref) : ResourceWeakRef<T>(ref) {}
ActorWeakSelfRef(const ResourceWeakRef<T>& ref) : ResourceWeakRef<T>(ref) {}
ActorWeakSelfRef(const ActorWeakSelfRef<T>& ref)
: ResourceWeakRef<T>(static_cast<const ResourceWeakRef<T>&>(ref)) {}
// Retrieves the resource as a pointer, throws operation_cancelled if the resource is not available
T* operator->() const {
if (!ResourceOwningRef<T>::available())
throw operation_cancelled();
return ResourceOwningRef<T>::resourceRef->resource;
}
// Gets the reference to the resource, Throws operation_cancelled if the resource is not available
T& operator*() const { return *(this->operator->()); }
};
#endif // FLOW_OWNING_REOSURCE_H