mirror of
https://github.com/apple/foundationdb.git
synced 2025-05-28 02:48:09 +08:00
155 lines
5.6 KiB
C++
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
|