summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects/managed.h
blob: 9653efa1c2949a3a4af493d97e0114dc8b4cf005 (plain)
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
129
130
131
132
133
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_MANAGED_H_
#define V8_OBJECTS_MANAGED_H_

#include <memory>
#include "src/execution/isolate.h"
#include "src/handles/global-handles.h"
#include "src/handles/handles.h"
#include "src/heap/factory.h"
#include "src/objects/foreign.h"

namespace v8 {
namespace internal {

// Implements a doubly-linked lists of destructors for the isolate.
struct ManagedPtrDestructor {
  // Estimated size of external memory associated with the managed object.
  // This is used to adjust the garbage collector's heuristics upon
  // allocation and deallocation of a managed object.
  size_t estimated_size_ = 0;
  ManagedPtrDestructor* prev_ = nullptr;
  ManagedPtrDestructor* next_ = nullptr;
  void* shared_ptr_ptr_ = nullptr;
  void (*destructor_)(void* shared_ptr) = nullptr;
  Address* global_handle_location_ = nullptr;

  ManagedPtrDestructor(size_t estimated_size, void* shared_ptr_ptr,
                       void (*destructor)(void*))
      : estimated_size_(estimated_size),
        shared_ptr_ptr_(shared_ptr_ptr),
        destructor_(destructor) {}
};

// The GC finalizer of a managed object, which does not depend on
// the template parameter.
V8_EXPORT_PRIVATE void ManagedObjectFinalizer(
    const v8::WeakCallbackInfo<void>& data);

// {Managed<T>} is essentially a {std::shared_ptr<T>} allocated on the heap
// that can be used to manage the lifetime of C++ objects that are shared
// across multiple isolates.
// When a {Managed<T>} object is garbage collected (or an isolate which
// contains {Managed<T>} is torn down), the {Managed<T>} deletes its underlying
// {std::shared_ptr<T>}, thereby decrementing its internal reference count,
// which will delete the C++ object when the reference count drops to 0.
template <class CppType>
class Managed : public Foreign {
 public:
  Managed() : Foreign() {}
  explicit Managed(Address ptr) : Foreign(ptr) {}
  Managed* operator->() { return this; }

  // Get a raw pointer to the C++ object.
  V8_INLINE CppType* raw() { return GetSharedPtrPtr()->get(); }

  // Get a reference to the shared pointer to the C++ object.
  V8_INLINE const std::shared_ptr<CppType>& get() { return *GetSharedPtrPtr(); }

  static Managed cast(Object obj) { return Managed(obj.ptr()); }
  static Managed unchecked_cast(Object obj) { return bit_cast<Managed>(obj); }

  // Allocate a new {CppType} and wrap it in a {Managed<CppType>}.
  template <typename... Args>
  static Handle<Managed<CppType>> Allocate(Isolate* isolate,
                                           size_t estimated_size,
                                           Args&&... args) {
    return FromSharedPtr(
        isolate, estimated_size,
        std::make_shared<CppType>(std::forward<Args>(args)...));
  }

  // Create a {Managed<CppType>} from an existing raw {CppType*}. The returned
  // object will now own the memory pointed to by {CppType}.
  static Handle<Managed<CppType>> FromRawPtr(Isolate* isolate,
                                             size_t estimated_size,
                                             CppType* ptr) {
    return FromSharedPtr(isolate, estimated_size,
                         std::shared_ptr<CppType>{ptr});
  }

  // Create a {Managed<CppType>} from an existing {std::unique_ptr<CppType>}.
  // The returned object will now own the memory pointed to by {CppType}, and
  // the unique pointer will be released.
  static Handle<Managed<CppType>> FromUniquePtr(
      Isolate* isolate, size_t estimated_size,
      std::unique_ptr<CppType> unique_ptr) {
    return FromSharedPtr(isolate, estimated_size, std::move(unique_ptr));
  }

  // Create a {Managed<CppType>} from an existing {std::shared_ptr<CppType>}.
  static Handle<Managed<CppType>> FromSharedPtr(
      Isolate* isolate, size_t estimated_size,
      const std::shared_ptr<CppType>& shared_ptr) {
    reinterpret_cast<v8::Isolate*>(isolate)
        ->AdjustAmountOfExternalAllocatedMemory(estimated_size);
    auto destructor = new ManagedPtrDestructor(
        estimated_size, new std::shared_ptr<CppType>{shared_ptr}, Destructor);
    Handle<Managed<CppType>> handle = Handle<Managed<CppType>>::cast(
        isolate->factory()->NewForeign(reinterpret_cast<Address>(destructor)));
    Handle<Object> global_handle = isolate->global_handles()->Create(*handle);
    destructor->global_handle_location_ = global_handle.location();
    GlobalHandles::MakeWeak(destructor->global_handle_location_, destructor,
                            &ManagedObjectFinalizer,
                            v8::WeakCallbackType::kParameter);
    isolate->RegisterManagedPtrDestructor(destructor);
    return handle;
  }

 private:
  // Internally this {Foreign} object stores a pointer to a new
  // std::shared_ptr<CppType>.
  std::shared_ptr<CppType>* GetSharedPtrPtr() {
    auto destructor =
        reinterpret_cast<ManagedPtrDestructor*>(foreign_address());
    return reinterpret_cast<std::shared_ptr<CppType>*>(
        destructor->shared_ptr_ptr_);
  }

  // Called by either isolate shutdown or the {ManagedObjectFinalizer} in order
  // to actually delete the shared pointer and decrement the shared refcount.
  static void Destructor(void* ptr) {
    auto shared_ptr_ptr = reinterpret_cast<std::shared_ptr<CppType>*>(ptr);
    delete shared_ptr_ptr;
  }
};

}  // namespace internal
}  // namespace v8

#endif  // V8_OBJECTS_MANAGED_H_