1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #pragma once
9 #ifdef __cplusplus
10 
11 #include <cassert>
12 #include <string>
13 #include <memory>
14 #include <mutex>
15 #include <unordered_set>
16 
17 #import <ABI48_0_0jsi/ABI48_0_0jsi.h>
18 #import <ABI48_0_0ReactCommon/ABI48_0_0CallInvoker.h>
19 
20 using namespace ABI48_0_0facebook;
21 using CallInvoker = ABI48_0_0facebook::ABI48_0_0React::CallInvoker;
22 
23 /**
24  * NOTE: This file is a mix of ReactCommon/TurboModuleUtils.h and ReactCommon/LongLivedObject.h
25  * Copying it here is needed until we upgrade RN to 0.66 that includes this commit:
26  * https://github.com/facebook/react-native/commit/32bfd7a857c23dd417f940d0c09843de257f6c61
27  * After that we can just use <ReactCommon/TurboModuleUtils.h>
28  *
29  * We need to wrap it in another napespace, because it would conflict with existing RN implementation
30  */
31 namespace ABI48_0_0expo {
32 
33 /**
34  * A simple wrapper class that can be registered to a collection that  keep it
35  * alive for extended period of time. This object can be removed from the
36  * collection when needed.
37  *
38  * The subclass of this class must be created using std::make_shared<T>().
39  * After creation, add it to the `LongLivedObjectCollection`.
40  * When done with the object, call `allowRelease()` to allow the OS to release
41  * it.
42  */
43 class LongLivedObject {
44  public:
45   virtual void allowRelease();
46 
47  protected:
48   LongLivedObject() {}
49   virtual ~LongLivedObject() {}
50 };
51 
52 /**
53  * A singleton, thread-safe, write-only collection for the `LongLivedObject`s.
54  */
55 class LongLivedObjectCollection {
56  public:
57   static LongLivedObjectCollection &get();
58 
59   LongLivedObjectCollection() {}
60   LongLivedObjectCollection(LongLivedObjectCollection const &) = delete;
61   void operator=(LongLivedObjectCollection const &) = delete;
62 
63   void add(std::shared_ptr<LongLivedObject> o) const;
64   void remove(const LongLivedObject *o) const;
65   void clear() const;
66 
67  private:
68   mutable std::unordered_set<std::shared_ptr<LongLivedObject>> collection_;
69   mutable std::mutex collectionMutex_;
70 };
71 
72 // Helper for passing jsi::Function arg to other methods.
73 class CallbackWrapper : public LongLivedObject {
74  private:
75   CallbackWrapper(
76       jsi::Function &&callback,
77       jsi::Runtime &runtime,
78       std::shared_ptr<CallInvoker> jsInvoker)
79       : longLivedObjectCollection_(),
80         callback_(std::move(callback)),
81         runtime_(runtime),
82         jsInvoker_(std::move(jsInvoker)) {}
83 
84   CallbackWrapper(
85       std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection,
86       jsi::Function &&callback,
87       jsi::Runtime &runtime,
88       std::shared_ptr<CallInvoker> jsInvoker)
89       : longLivedObjectCollection_(longLivedObjectCollection),
90         callback_(std::move(callback)),
91         runtime_(runtime),
92         jsInvoker_(std::move(jsInvoker)) {}
93 
94   // Use a weak_ptr to avoid a retain cycle: LongLivedObjectCollection owns all
95   // CallbackWrappers. So, CallbackWrapper cannot own its
96   // LongLivedObjectCollection.
97   std::weak_ptr<LongLivedObjectCollection> longLivedObjectCollection_;
98   jsi::Function callback_;
99   jsi::Runtime &runtime_;
100   std::shared_ptr<CallInvoker> jsInvoker_;
101 
102  public:
103   static std::weak_ptr<CallbackWrapper> createWeak(
104       jsi::Function &&callback,
105       jsi::Runtime &runtime,
106       std::shared_ptr<CallInvoker> jsInvoker) {
107     auto wrapper = std::shared_ptr<CallbackWrapper>(
108         new CallbackWrapper(std::move(callback), runtime, jsInvoker));
109     LongLivedObjectCollection::get().add(wrapper);
110     return wrapper;
111   }
112 
113   static std::weak_ptr<CallbackWrapper> createWeak(
114       std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection,
115       jsi::Function &&callback,
116       jsi::Runtime &runtime,
117       std::shared_ptr<CallInvoker> jsInvoker) {
118     auto wrapper = std::shared_ptr<CallbackWrapper>(new CallbackWrapper(
119         longLivedObjectCollection, std::move(callback), runtime, jsInvoker));
120     longLivedObjectCollection->add(wrapper);
121     return wrapper;
122   }
123 
124   // Delete the enclosed jsi::Function
125   void destroy() {
126     allowRelease();
127   }
128 
129   jsi::Function &callback() {
130     return callback_;
131   }
132 
133   jsi::Runtime &runtime() {
134     return runtime_;
135   }
136 
137   CallInvoker &jsInvoker() {
138     return *(jsInvoker_);
139   }
140 
141   void allowRelease() override {
142     if (auto longLivedObjectCollection = longLivedObjectCollection_.lock()) {
143       if (longLivedObjectCollection != nullptr) {
144         longLivedObjectCollection->remove(this);
145         return;
146       }
147     }
148     LongLivedObject::allowRelease();
149   }
150 };
151 
152 }
153 
154 #endif // __cplusplus
155