Public Member Functions | List of all members
BPrivate::Network::BExclusiveBorrow< T > Class Template Reference

Smart pointer that allows shared ownership of an object with exclusive access. More...

Public Member Functions

 BExclusiveBorrow () noexcept
 Create a new smart pointer with no value. More...
 
 BExclusiveBorrow (BExclusiveBorrow &&other) noexcept
 Move constructor. More...
 
 BExclusiveBorrow (nullptr_t) noexcept
 Special constructor that creates a new smart pointer with no value. More...
 
 BExclusiveBorrow (T *object)
 Create a new smart pointer that takes ownership of the object. More...
 
 ~BExclusiveBorrow ()
 Destructor. More...
 
bool HasValue () const noexcept
 Check if the object has a value or is empty. More...
 
T & operator* () const
 Dereferences the pointer. More...
 
T * operator-> () const
 Dereferences the pointer. More...
 
BExclusiveBorrowoperator= (BExclusiveBorrow &&other) noexcept
 Move assignment. More...
 
std::unique_ptr< T > Release ()
 Returns a unique_ptr of the inner object and releases the ownership. More...
 

Detailed Description

template<typename T>
class BPrivate::Network::BExclusiveBorrow< T >

Smart pointer that allows shared ownership of an object with exclusive access.

This smart pointer was designed to support the particular pattern where a non-threadsafe or non-lockable needs to be shared between two threads, where only one can have access to the underlying object at the time.

When creating a new object, the underlying object can be accessed using the dereference operator overloads as if with any other smart pointer. This ownership can then be borrowed by creating a BBorrow object. At that stage, the original owner can no longer access the underlying object, until the borrow is returned. The borrow can access the object as long as they retain the borrow. The borrow is returned by the borrow object going out of scope, or by the borrow object being assigned a nullptr object. At that stage, the original owner regains access.

// Create a newly owned string object.
BExclusiveBorrow<BString> owner = make_exclusive_borrow<BString>("Initial value");
// Access the exclusively accessibe object and set it to a different value
owner->SetTo("New value set by owner");
// Create a borrow.
BBorrow<BString> borrow = BBorrow<BString>(owner);
try {
owner->SetTo("Another value set by owner");
} catch (const BorrowError& e) {
// This error will be thrown because the `owner` cannot access the object while borrowed.
}
try {
BBorrow<BString> secondBorrow = BBorrow<BString>(owner);
} catch (const BorrowError& e) {
// This error will be thrown because there cannot be more than one borrow at a time.
}
// The `borrow` has exclusive access to the underlying BString object
borrow->SetTo("A value set by the borrower");
// The borrow is returned by explicitly setting it to `nullptr` or by having the object go out
// of scope.
borrow = nullptr;
// The owner can access the object again
assert(*owner == "A value set by the borrower");
Object Lifetime Management
The BExclusiveBorrow and BBorrow pair manage the lifetime of the underlying object, meaning the memory will be freed when the object is no longer referenced by either the owner or the borrower. It is possible to get the ownership of the underlying object through the BExclusiveBorrow::Release() method. This returns a unique_ptr.
Creating New Objects
When creating a BExclusiveBorrow object, you can use the BExclusiveBorrow(T* object) constructor to create a new smart pointer that takes an existing underlying object. Note that the smart pointer will assume ownership, meaning that you should also have ownership of that object. If you want to create a BExclusiveBorrow object for a new object, then you can use the make_exclusive_borrow() function to create a new object.
Move Semantics and Empty Objects
The template class is designed around having an underlying object value, and in most cases will have an underlying object. However, there may be cases where a BExclusiveOwner or BBorrow object will not have an internal value. This either happens when it is explicitly assigned an empty value, or after the object has been moved. You can check whether the object has a value through the HasValue() method. Trying to access an empty object will throw a BBorrowError.
Checked Access
The semantics of the exclusive ownership are enforced by this class. The rules are:
  • There can only be one owner. The object cannot be copied, only moved.
  • There can only be one borrower at a time. The borrow object cannot be copied, only moved.
  • If one tries to create an additional borrow, an exception is thrown.
  • If an object is borrowed, accessing it through the owner will throw exceptions.
Casting Pointers between Owner and Borrower
For some design patterns, you may want to be able to cast the type of the owner to a related type for the borrower. For example, the Network Services kit accepts a BBorrow<BDataIO> type in order to allow the user to specify where to write the content of a network request to. The BDataIO itself is an abstract interface to read and write data from an object. A user will most likely use a BFile or BMallocIO as underlying objects, both of which have BDataIO as their base class.
Due to the specialized constructor of BBorrow, it is possible to cast between compatible pointer types, without loosing the advantages of properly cleaning up the object when the borrow and the owner go out of scope. In the internals of the template, a type erasure technique similar to that of std::shared_ptr is used.
// Create a new BFile object, which inherits the BDataIO class.
BExclusiveBorrow<BFile> owner = make_exclusive_borrow<BFile>("path/to/file", B_READ_WRITE);
// The following succeeds because a BFile pointer can be assigned to a BDataIO pointer.
BBorrow<BDataIO> borrow = BBorrow<BDataIO>(owner);
Multithread Safety, and Performance Cost
This smart object uses atomics to synchronize the ownership and borrows of the object, and to enforce all the checks that were mentioned previously. The atomics guarantee that when you want to access the object in BExclusiveBorrow, that this only succeeds after any outstanding borrow is completed, otherwise an exception is thrown. While atomics can be used as a method of synchronization, this templace class is not designed for that and it does not have the tools to help doing that. If you need to synchronize object access between through threads, you should use semaphores or thread joins instead.
Template Parameters
TThe type of object for this smart pointer.
Since
Haiku R1

Constructor & Destructor Documentation

◆ BExclusiveBorrow() [1/4]

template<typename T>
BPrivate::Network::BExclusiveBorrow< T >::BExclusiveBorrow ( )
inlinenoexcept

Create a new smart pointer with no value.

Since
Haiku R1

◆ BExclusiveBorrow() [2/4]

template<typename T>
BPrivate::Network::BExclusiveBorrow< T >::BExclusiveBorrow ( nullptr_t  )
inlinenoexcept

Special constructor that creates a new smart pointer with no value.

Since
Haiku R1

◆ BExclusiveBorrow() [3/4]

template<typename T>
BPrivate::Network::BExclusiveBorrow< T >::BExclusiveBorrow ( T *  object)
inline

Create a new smart pointer that takes ownership of the object.

Parameters
objectThe object to wrap inside this smart pointer.
Exceptions
std::bad_allocIn case there are issues allocating memory for the internals of the smart pointer.
Since
Haiku R1

◆ ~BExclusiveBorrow()

template<typename T>
BPrivate::Network::BExclusiveBorrow< T >::~BExclusiveBorrow ( )
inline

Destructor.

If the smart pointer is not empty, the underlying object will be deleted if there no longer is a borrow accessing it.

Since
Haiku R1

◆ BExclusiveBorrow() [4/4]

template<typename T>
BPrivate::Network::BExclusiveBorrow< T >::BExclusiveBorrow ( BExclusiveBorrow< T > &&  other)
inlinenoexcept

Move constructor.

Parameters
otherThe object to move from. It will be left empty after the move.
Since
Haiku R1

Member Function Documentation

◆ HasValue()

template<typename T>
bool BPrivate::Network::BExclusiveBorrow< T >::HasValue ( ) const
inlinenoexcept

Check if the object has a value or is empty.

Since
Haiku R1

◆ operator*()

template<typename T>
T & BPrivate::Network::BExclusiveBorrow< T >::operator* ( ) const
inline

Dereferences the pointer.

Exceptions
BBorrowErrorThis exception is raised if the object is borrowed, or if it is empty.
Since
Haiku R1

◆ operator->()

template<typename T>
T * BPrivate::Network::BExclusiveBorrow< T >::operator-> ( ) const
inline

Dereferences the pointer.

Exceptions
BBorrowErrorThis exception is raised if the object is borrowed, or if it is empty.
Since
Haiku R1

◆ operator=()

template<typename T>
BExclusiveBorrow & BPrivate::Network::BExclusiveBorrow< T >::operator= ( BExclusiveBorrow< T > &&  other)
inlinenoexcept

Move assignment.

Parameters
otherThe object to move from. It will be left empty after the move.
Since
Haiku R1

◆ Release()

template<typename T>
std::unique_ptr< T > BPrivate::Network::BExclusiveBorrow< T >::Release ( )
inline

Returns a unique_ptr of the inner object and releases the ownership.

Exceptions
BBorrowErrorThis exception is raised if the object is borrowed, or if it is empty.
Since
Haiku R1