aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/yt/memory/new.h
blob: ea96fd60e0e956f039e89777770baff817858faf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#pragma once

#include "intrusive_ptr.h"
#include "ref_tracked.h"

#include <library/cpp/yt/misc/source_location.h>

#include <util/system/defaults.h>

namespace NYT {

////////////////////////////////////////////////////////////////////////////////

/*!
 * \defgroup yt_new New<T> safe smart pointer constructors
 * \ingroup yt_new
 *
 * This is collection of safe smart pointer constructors.
 *
 * \page yt_new_rationale Rationale
 * New<T> function family was designed to prevent the following problem.
 * Consider the following piece of code.
 *
 * \code
 *     class TFoo
 *         : public virtual TRefCounted
 *     {
 *     public:
 *         TFoo();
 *     };
 *
 *     typedef TIntrusivePtr<TFoo> TFooPtr;
 *
 *     void RegisterObject(TFooPtr foo)
 *     {
 *         ...
 *     }
 *
 *     TFoo::TFoo()
 *     {
 *         // ... do something before
 *         RegisterObject(this);
 *         // ... do something after
 *     }
 * \endcode
 *
 * What will happen on <tt>new TFoo()</tt> construction? After memory allocation
 * the reference counter for newly created instance would be initialized to zero.
 * Afterwards, the control goes to TFoo constructor. To invoke
 * <tt>RegisterObject</tt> a new temporary smart pointer to the current instance
 * have to be created effectively incrementing the reference counter (now one).
 * After <tt>RegisterObject</tt> returns the control to the constructor
 * the temporary pointer is destroyed effectively decrementing the reference
 * counter to zero hence triggering object destruction during its initialization.
 *
 * To avoid this undefined behavior <tt>New<T></tt> was introduced.
 * <tt>New<T></tt> holds a fake
 * reference to the object during its construction effectively preventing
 * premature destruction.
 *
 * \note An initialization like <tt>TIntrusivePtr&lt;T&gt; p = new T()</tt>
 * would result in a dangling reference due to internals of #New<T> and
 * #TRefCountedBase.
 */

////////////////////////////////////////////////////////////////////////////////

template <class T, class = void>
struct THasAllocator
{
    using TFalse = void;
};

template <class T>
struct THasAllocator<T, std::void_t<typename T::TAllocator>>
{
    using TTrue = void;
};

////////////////////////////////////////////////////////////////////////////////

//! Allocates a new instance of |T|.
template <class T, class... As, class = typename THasAllocator<T>::TFalse>
TIntrusivePtr<T> New(As&&... args);

template <class T, class... As, class = typename THasAllocator<T>::TTrue>
TIntrusivePtr<T> New(typename T::TAllocator* allocator, As&&... args);

//! Allocates an instance of |T| with additional storage of #extraSpaceSize bytes.
template <class T, class... As, class = typename THasAllocator<T>::TFalse>
TIntrusivePtr<T> NewWithExtraSpace(size_t extraSpaceSize, As&&... args);

template <class T, class... As, class = typename THasAllocator<T>::TTrue>
TIntrusivePtr<T> NewWithExtraSpace(typename T::TAllocator* allocator, size_t extraSpaceSize, As&&... args);

//! Allocates a new instance of |T| with user deleter.
template <class T, class TDeleter, class... As>
TIntrusivePtr<T> NewWithDelete(const TDeleter& deleter, As&&... args);

//! Allocates a new instance of |T|.
//! The allocation is additionally marked with #location.
template <class T, class TTag, int Counter, class... As>
TIntrusivePtr<T> NewWithLocation(const TSourceLocation& location, As&&... args);

//! Enables calling #New and co for types with private ctors.
#define DECLARE_NEW_FRIEND() \
    template <class DECLARE_NEW_FRIEND_T> \
    friend struct NYT::TRefCountedWrapper;

////////////////////////////////////////////////////////////////////////////////

//! CRTP mixin enabling access to instance's extra space.
template <class T>
class TWithExtraSpace
{
protected:
    const void* GetExtraSpacePtr() const;
    void* GetExtraSpacePtr();
    size_t GetUsableSpaceSize() const;
};

////////////////////////////////////////////////////////////////////////////////

} // namespace NYT

#define NEW_INL_H_
#include "new-inl.h"
#undef NEW_INL_H_