react-native-wgpu 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,8 +46,7 @@ add_library(${PACKAGE_NAME} SHARED
46
46
  ../cpp/rnwgpu/RNWebGPUManager.cpp
47
47
  ../cpp/jsi/RNFPromise.cpp
48
48
  ../cpp/jsi/RNFHybridObject.cpp
49
- ../cpp/jsi/RNFRuntimeCache.cpp
50
- ../cpp/jsi/RNFWorkletRuntimeRegistry.cpp
49
+ ../cpp/jsi/RNFRuntimeState.cpp
51
50
  ../cpp/rnwgpu/async/AsyncRunner.cpp
52
51
  ../cpp/rnwgpu/async/AsyncTaskHandle.cpp
53
52
  ../cpp/rnwgpu/async/JSIMicrotaskDispatcher.cpp
@@ -129,6 +129,7 @@ void HybridObject::ensureInitialized(facebook::jsi::Runtime& runtime) {
129
129
  if (!_didLoadMethods) {
130
130
  [[unlikely]];
131
131
  _creationRuntime = &runtime;
132
+ _runtimeState = rnwgpu::RNFRuntimeState::get(runtime);
132
133
  // lazy-load all exposed methods
133
134
  loadHybridMethods();
134
135
  _didLoadMethods = true;
@@ -136,11 +137,14 @@ void HybridObject::ensureInitialized(facebook::jsi::Runtime& runtime) {
136
137
  }
137
138
 
138
139
  bool HybridObject::isRuntimeAlive() {
139
- if (_creationRuntime == nullptr) {
140
- [[unlikely]];
141
- return false;
140
+ return !_runtimeState.expired();
141
+ }
142
+
143
+ facebook::jsi::Runtime* HybridObject::getCreationRuntime() const {
144
+ if (_runtimeState.expired()) {
145
+ return nullptr;
142
146
  }
143
- return RNFWorkletRuntimeRegistry::isRuntimeAlive(_creationRuntime);
147
+ return _creationRuntime;
144
148
  }
145
149
 
146
150
  } // namespace margelo
@@ -5,7 +5,7 @@
5
5
  #pragma once
6
6
 
7
7
  #include "WGPULogger.h"
8
- #include "RNFWorkletRuntimeRegistry.h"
8
+ #include "RNFRuntimeState.h"
9
9
  #include <functional>
10
10
  #include <jsi/jsi.h>
11
11
  #include <memory>
@@ -91,8 +91,9 @@ private:
91
91
 
92
92
  protected:
93
93
  const char* _name = TAG;
94
- // Store a pointer to the runtime. Needed for checking if the runtime is still active, see WorkletRuntimeRegistry.
94
+ // Store a pointer to the runtime for convenience; ensure it is only used while the runtime state is alive.
95
95
  jsi::Runtime* _creationRuntime = nullptr;
96
+ std::weak_ptr<rnwgpu::RNFRuntimeState> _runtimeState;
96
97
 
97
98
  private:
98
99
  inline void ensureInitialized(facebook::jsi::Runtime& runtime);
@@ -128,6 +129,10 @@ private:
128
129
  };
129
130
  }
130
131
 
132
+ protected:
133
+ facebook::jsi::Runtime* getCreationRuntime() const;
134
+ std::weak_ptr<rnwgpu::RNFRuntimeState> getRuntimeStateWeak() const { return _runtimeState; }
135
+
131
136
  protected:
132
137
  template <typename Derived, typename ReturnType, typename... Args>
133
138
  void registerHybridMethod(std::string name, ReturnType (Derived::*method)(Args...), Derived* derivedInstance, bool override = false) {
@@ -22,7 +22,6 @@
22
22
  #include "RNFEnumMapper.h"
23
23
  #include "RNFJSIHelper.h"
24
24
  #include "RNFPromise.h"
25
- #include "RNFWorkletRuntimeRegistry.h"
26
25
 
27
26
  #include "Unions.h"
28
27
 
@@ -225,52 +224,6 @@ template <> struct JSIConverter<rnwgpu::async::AsyncTaskHandle> {
225
224
  }
226
225
  };
227
226
 
228
-
229
- // [](Args...) -> T {} <> (Args...) => T
230
- template <typename ReturnType, typename... Args> struct JSIConverter<std::function<ReturnType(Args...)>> {
231
- static std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
232
- jsi::Function function = arg.asObject(runtime).asFunction(runtime);
233
-
234
- std::shared_ptr<jsi::Function> sharedFunction = JSIHelper::createSharedJsiFunction(runtime, std::move(function));
235
- return [&runtime, sharedFunction, outOfBound](Args... args) -> ReturnType {
236
- jsi::Value result = sharedFunction->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
237
- if constexpr (std::is_same_v<ReturnType, void>) {
238
- // it is a void function (returns undefined)
239
- return;
240
- } else {
241
- // it returns a custom type, parse it from the JSI value.
242
- return JSIConverter<ReturnType>::fromJSI(runtime, std::move(result), outOfBound);
243
- }
244
- };
245
- }
246
-
247
- template <size_t... Is>
248
- static jsi::Value callHybridFunction(const std::function<ReturnType(Args...)>& function, jsi::Runtime& runtime, const jsi::Value* args,
249
- std::index_sequence<Is...>, size_t count) {
250
- if constexpr (std::is_same_v<ReturnType, void>) {
251
- // it is a void function (will return undefined in JS)
252
- function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is], Is >= count)...);
253
- return jsi::Value::undefined();
254
- } else {
255
- // it is a custom type, parse it to a JS value
256
- ReturnType result = function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is], Is >= count)...);
257
- return JSIConverter<ReturnType>::toJSI(runtime, result);
258
- }
259
- }
260
- static jsi::Value toJSI(jsi::Runtime& runtime, const std::function<ReturnType(Args...)>& function) {
261
- jsi::HostFunctionType jsFunction = [function = std::move(function)](jsi::Runtime& runtime, const jsi::Value& thisValue,
262
- const jsi::Value* args, size_t count) -> jsi::Value {
263
- if (count != sizeof...(Args)) {
264
- [[unlikely]];
265
- throw jsi::JSError(runtime, "Function expected " + std::to_string(sizeof...(Args)) + " arguments, but received " +
266
- std::to_string(count) + "!");
267
- }
268
- return callHybridFunction(function, runtime, args, std::index_sequence_for<Args...>{}, count);
269
- };
270
- return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "hostFunction"), sizeof...(Args), jsFunction);
271
- }
272
- };
273
-
274
227
  // std::map<std::string, T> <> Record<string, T>
275
228
  template <typename ValueType> struct JSIConverter<std::map<std::string, ValueType>> {
276
229
  static std::map<std::string, ValueType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg, bool outOfBound) {
@@ -4,7 +4,7 @@
4
4
 
5
5
  #pragma once
6
6
 
7
- #include "RNFWorkletRuntimeRegistry.h"
7
+ #include "RNFRuntimeState.h"
8
8
  #include <jsi/jsi.h>
9
9
  #include <memory>
10
10
  #include <utility>
@@ -20,8 +20,10 @@ public:
20
20
  * Every jsi::Function you intend to share or hold should be wrapped using this function.
21
21
  */
22
22
  static std::shared_ptr<jsi::Function> createSharedJsiFunction(jsi::Runtime& runtime, jsi::Function&& function) {
23
- std::shared_ptr<jsi::Function> sharedFunction(new jsi::Function(std::move(function)), [&runtime](jsi::Function* ptr) {
24
- if (RNFWorkletRuntimeRegistry::isRuntimeAlive(&runtime)) {
23
+ auto runtimeState = rnwgpu::RNFRuntimeState::get(runtime);
24
+ std::weak_ptr<rnwgpu::RNFRuntimeState> runtimeStateWeak = runtimeState;
25
+ std::shared_ptr<jsi::Function> sharedFunction(new jsi::Function(std::move(function)), [runtimeStateWeak](jsi::Function* ptr) {
26
+ if (!runtimeStateWeak.expired()) {
25
27
  // Only delete the jsi::Function when the runtime it created is still alive.
26
28
  // Otherwise leak memory. We do this on purpose, as sometimes we would keep
27
29
  // references to JSI objects past the lifetime of its runtime (e.g.,
@@ -0,0 +1,18 @@
1
+ #include "RNFRuntimeState.h"
2
+
3
+ namespace rnwgpu {
4
+
5
+ const facebook::jsi::UUID RNFRuntimeState::kRuntimeStateKey = facebook::jsi::UUID();
6
+
7
+ std::shared_ptr<RNFRuntimeState> RNFRuntimeState::get(facebook::jsi::Runtime& runtime) {
8
+ auto existing = runtime.getRuntimeData(kRuntimeStateKey);
9
+ if (existing) {
10
+ return std::static_pointer_cast<RNFRuntimeState>(existing);
11
+ }
12
+
13
+ auto state = std::shared_ptr<RNFRuntimeState>(new RNFRuntimeState());
14
+ runtime.setRuntimeData(kRuntimeStateKey, state);
15
+ return state;
16
+ }
17
+
18
+ } // namespace rnwgpu
@@ -0,0 +1,106 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+
5
+ #include <memory>
6
+ #include <mutex>
7
+ #include <unordered_map>
8
+ #include <unordered_set>
9
+
10
+ namespace rnwgpu {
11
+
12
+ namespace jsi = facebook::jsi;
13
+
14
+ /**
15
+ * Runtime state management using Hermes' runtime.setRuntimeData API.
16
+ * This replaces the old RuntimeLifecycleMonitor/RuntimeAwareCache system.
17
+ */
18
+ class RNFRuntimeState {
19
+ public:
20
+ // UUID key for storing our runtime state
21
+ static const jsi::UUID kRuntimeStateKey;
22
+
23
+ /**
24
+ * Get or create the runtime state for the given runtime
25
+ */
26
+ static std::shared_ptr<RNFRuntimeState> get(jsi::Runtime& runtime);
27
+
28
+ /**
29
+ * Template cache that can store any type T per object pointer
30
+ */
31
+ template <typename T>
32
+ class ObjectCache {
33
+ public:
34
+ std::shared_ptr<T> getOrCreate(void* object) {
35
+ std::lock_guard<std::mutex> lock(mutex_);
36
+ auto it = cache_.find(object);
37
+ if (it != cache_.end()) {
38
+ return it->second;
39
+ }
40
+
41
+ auto value = std::make_shared<T>();
42
+ cache_[object] = value;
43
+ return value;
44
+ }
45
+
46
+ void remove(void* object) {
47
+ std::lock_guard<std::mutex> lock(mutex_);
48
+ cache_.erase(object);
49
+ }
50
+
51
+ void clear() {
52
+ std::lock_guard<std::mutex> lock(mutex_);
53
+ cache_.clear();
54
+ }
55
+
56
+ private:
57
+ std::mutex mutex_;
58
+ std::unordered_map<void*, std::shared_ptr<T>> cache_;
59
+ };
60
+
61
+ /**
62
+ * Get or create a cache for a specific type T
63
+ */
64
+ template <typename T>
65
+ std::shared_ptr<ObjectCache<T>> getCache() {
66
+ std::lock_guard<std::mutex> lock(mutex_);
67
+
68
+ // Use type_info as key for the cache type
69
+ const std::type_info& typeId = typeid(T);
70
+ auto it = typeCaches_.find(&typeId);
71
+
72
+ if (it != typeCaches_.end()) {
73
+ return std::static_pointer_cast<ObjectCache<T>>(it->second);
74
+ }
75
+
76
+ auto cache = std::make_shared<ObjectCache<T>>();
77
+ typeCaches_[&typeId] = cache;
78
+ return cache;
79
+ }
80
+
81
+ private:
82
+ RNFRuntimeState() = default;
83
+
84
+ std::mutex mutex_;
85
+ // Map from type_info to cache instance
86
+ std::unordered_map<const std::type_info*, std::shared_ptr<void>> typeCaches_;
87
+ };
88
+
89
+ /**
90
+ * Template helper for runtime-aware caching compatible with the old API
91
+ * This provides a migration path from RuntimeAwareCache
92
+ */
93
+ template <typename T>
94
+ class RuntimeAwareCache {
95
+ public:
96
+ T& get(jsi::Runtime& rt) {
97
+ auto state = RNFRuntimeState::get(rt);
98
+ auto cache = state->getCache<T>();
99
+
100
+ // For compatibility, we use the runtime pointer as the object key
101
+ auto ptr = cache->getOrCreate(&rt);
102
+ return *ptr;
103
+ }
104
+ };
105
+
106
+ } // namespace rnwgpu
@@ -79,18 +79,19 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
79
79
  std::string label =
80
80
  descriptor.has_value() ? descriptor.value()->label.value_or("") : "";
81
81
 
82
+ auto creationRuntime = getCreationRuntime();
82
83
  return _async->postTask(
83
84
  [this, aDescriptor, descriptor, label = std::move(label),
84
- deviceLostBinding](
85
- const async::AsyncTaskHandle::ResolveFunction &resolve,
86
- const async::AsyncTaskHandle::RejectFunction &reject) {
85
+ deviceLostBinding,
86
+ creationRuntime](const async::AsyncTaskHandle::ResolveFunction &resolve,
87
+ const async::AsyncTaskHandle::RejectFunction &reject) {
87
88
  (void)descriptor;
88
89
  _instance.RequestDevice(
89
90
  &aDescriptor, wgpu::CallbackMode::AllowProcessEvents,
90
- [asyncRunner = _async, resolve, reject, label,
91
- creationRuntime = _creationRuntime, deviceLostBinding](
92
- wgpu::RequestDeviceStatus status, wgpu::Device device,
93
- wgpu::StringView message) mutable {
91
+ [asyncRunner = _async, resolve, reject, label, creationRuntime,
92
+ deviceLostBinding](wgpu::RequestDeviceStatus status,
93
+ wgpu::Device device,
94
+ wgpu::StringView message) mutable {
94
95
  if (message.length) {
95
96
  fprintf(stderr, "%s", message.data);
96
97
  }
@@ -106,6 +107,9 @@ async::AsyncTaskHandle GPUAdapter::requestDevice(
106
107
  device.SetLoggingCallback([creationRuntime](
107
108
  wgpu::LoggingType type,
108
109
  wgpu::StringView msg) {
110
+ if (creationRuntime == nullptr) {
111
+ return;
112
+ }
109
113
  const char *logLevel = "";
110
114
  switch (type) {
111
115
  case wgpu::LoggingType::Warning:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-wgpu",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "React Native WebGPU",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -1,57 +0,0 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
- #include <jsi/jsi.h>
5
-
6
- #include <memory>
7
- #include <unordered_map>
8
- #include <unordered_set>
9
- #include <utility>
10
-
11
- #include "RNFRuntimeCache.h"
12
-
13
- namespace margelo {
14
-
15
- static std::unordered_map<jsi::Runtime*, std::unordered_set<RuntimeLifecycleListener*>> listeners;
16
-
17
- struct RuntimeLifecycleMonitorObject : public jsi::HostObject {
18
- jsi::Runtime* _rt;
19
- explicit RuntimeLifecycleMonitorObject(jsi::Runtime* rt) : _rt(rt) {}
20
- ~RuntimeLifecycleMonitorObject() {
21
- auto listenersSet = listeners.find(_rt);
22
- if (listenersSet != listeners.end()) {
23
- for (auto listener : listenersSet->second) {
24
- listener->onRuntimeDestroyed(_rt);
25
- }
26
- listeners.erase(listenersSet);
27
- }
28
- }
29
- };
30
-
31
- void RuntimeLifecycleMonitor::addListener(jsi::Runtime& rt, RuntimeLifecycleListener* listener) {
32
- auto listenersSet = listeners.find(&rt);
33
- if (listenersSet == listeners.end()) {
34
- // We install a global host object in the provided runtime, this way we can
35
- // use that host object destructor to get notified when the runtime is being
36
- // terminated. We use a unique name for the object as it gets saved with the
37
- // runtime's global object.
38
- rt.global().setProperty(rt, "__rnfl_rt_lifecycle_monitor",
39
- jsi::Object::createFromHostObject(rt, std::make_shared<RuntimeLifecycleMonitorObject>(&rt)));
40
- std::unordered_set<RuntimeLifecycleListener*> newSet;
41
- newSet.insert(listener);
42
- listeners.emplace(&rt, std::move(newSet));
43
- } else {
44
- listenersSet->second.insert(listener);
45
- }
46
- }
47
-
48
- void RuntimeLifecycleMonitor::removeListener(jsi::Runtime& rt, RuntimeLifecycleListener* listener) {
49
- auto listenersSet = listeners.find(&rt);
50
- if (listenersSet == listeners.end()) {
51
- // nothing to do here
52
- } else {
53
- listenersSet->second.erase(listener);
54
- }
55
- }
56
-
57
- } // namespace margelo
@@ -1,79 +0,0 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
- #pragma once
5
-
6
- #include <jsi/jsi.h>
7
-
8
- #include <memory>
9
- #include <unordered_map>
10
- #include <unordered_set>
11
- #include <utility>
12
-
13
- namespace margelo {
14
-
15
- namespace jsi = facebook::jsi;
16
-
17
- /**
18
- * Listener interface that allows for getting notified when a jsi::Runtime
19
- * instance is destroyed.
20
- */
21
- struct RuntimeLifecycleListener {
22
- virtual ~RuntimeLifecycleListener() {}
23
- virtual void onRuntimeDestroyed(jsi::Runtime*) = 0;
24
- };
25
-
26
- /**
27
- * This class provides an API via static methods for registering and
28
- * unregistering runtime lifecycle listeners. The listeners can be used to
29
- * cleanup any data that references a given jsi::Runtime instance before it gets
30
- * destroyed.
31
- */
32
- struct RuntimeLifecycleMonitor {
33
- static void addListener(jsi::Runtime& rt, RuntimeLifecycleListener* listener);
34
- static void removeListener(jsi::Runtime& rt, RuntimeLifecycleListener* listener);
35
- };
36
-
37
- /**
38
- * Provides a way to keep data specific to a jsi::Runtime instance that gets
39
- * cleaned up when that runtime is destroyed. This is necessary because JSI does
40
- * not allow for its associated objects to be retained past the runtime
41
- * lifetime. If an object (e.g. jsi::Values or jsi::Function instances) is kept
42
- * after the runtime is torn down, its destructor (once it is destroyed
43
- * eventually) will result in a crash (JSI objects keep a pointer to memory
44
- * managed by the runtime, accessing that portion of the memory after runtime is
45
- * deleted is the root cause of that crash).
46
- */
47
- template <typename T> class RuntimeAwareCache : public RuntimeLifecycleListener {
48
-
49
- public:
50
- void onRuntimeDestroyed(jsi::Runtime* rt) override {
51
- // A runtime has been destroyed, so destroy the related cache.
52
- _runtimeCaches.erase(rt);
53
- }
54
-
55
- ~RuntimeAwareCache() {
56
- for (auto& cache : _runtimeCaches) {
57
- // remove all `onRuntimeDestroyed` listeners.
58
- RuntimeLifecycleMonitor::removeListener(*cache.first, this);
59
- }
60
- }
61
-
62
- T& get(jsi::Runtime& rt) {
63
- if (_runtimeCaches.count(&rt) == 0) {
64
- // This is the first time this Runtime has been accessed.
65
- // We set up a `onRuntimeDestroyed` listener for it and
66
- // initialize the cache map.
67
- RuntimeLifecycleMonitor::addListener(rt, this);
68
-
69
- T cache;
70
- _runtimeCaches.emplace(&rt, std::move(cache));
71
- }
72
- return _runtimeCaches.at(&rt);
73
- }
74
-
75
- private:
76
- std::unordered_map<jsi::Runtime*, T> _runtimeCaches;
77
- };
78
-
79
- } // namespace margelo
@@ -1,43 +0,0 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
- #pragma once
5
-
6
- #include "RNFWorkletRuntimeRegistry.h"
7
-
8
- #include <jsi/jsi.h>
9
-
10
- #include <memory>
11
-
12
- namespace margelo {
13
-
14
- // From:
15
- // https://github.com/software-mansion/react-native-reanimated/blob/6cb1a66f1a68cac8079de2b6b305d22359847e51/Common/cpp/ReanimatedRuntime/WorkletRuntimeCollector.h
16
- class WorkletRuntimeCollector : public jsi::HostObject {
17
- // When worklet runtime is created, we inject an instance of this class as a
18
- // `jsi::HostObject` into the global object. When worklet runtime is
19
- // terminated, the object is garbage-collected, which runs the C++ destructor.
20
- // In the destructor, we unregister the worklet runtime from the registry.
21
-
22
- public:
23
- explicit WorkletRuntimeCollector(jsi::Runtime& runtime) : _runtime(runtime) {
24
- Logger::log("WorkletRuntimeCollector", "Registering WorkletRuntime %p", &runtime);
25
- RNFWorkletRuntimeRegistry::registerRuntime(_runtime);
26
- }
27
-
28
- ~WorkletRuntimeCollector() {
29
- Logger::log("WorkletRuntimeCollector", "Unregistering WorkletRuntime %p", &_runtime);
30
- RNFWorkletRuntimeRegistry::unregisterRuntime(_runtime);
31
- }
32
-
33
- static void install(jsi::Runtime& rt) {
34
- auto collector = std::make_shared<WorkletRuntimeCollector>(rt);
35
- auto object = jsi::Object::createFromHostObject(rt, collector);
36
- rt.global().setProperty(rt, "__workletRuntimeCollector", object);
37
- }
38
-
39
- private:
40
- jsi::Runtime& _runtime;
41
- };
42
-
43
- } // namespace margelo
@@ -1,12 +0,0 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
- #include "RNFWorkletRuntimeRegistry.h"
5
- #include <set>
6
-
7
- namespace margelo {
8
-
9
- std::set<jsi::Runtime*> RNFWorkletRuntimeRegistry::registry_{};
10
- std::mutex RNFWorkletRuntimeRegistry::mutex_{};
11
-
12
- } // namespace margelo
@@ -1,44 +0,0 @@
1
- //
2
- // Created by Marc Rousavy on 22.02.24.
3
- //
4
- #pragma once
5
-
6
- #include <jsi/jsi.h>
7
-
8
- #include <mutex>
9
- #include <set>
10
-
11
- namespace jsi = facebook::jsi;
12
-
13
- namespace margelo {
14
-
15
- // From:
16
- // https://github.com/software-mansion/react-native-reanimated/blob/6cb1a66f1a68cac8079de2b6b305d22359847e51/Common/cpp/ReanimatedRuntime/WorkletRuntimeRegistry.h
17
- class RNFWorkletRuntimeRegistry {
18
- private:
19
- static std::set<jsi::Runtime*> registry_;
20
- static std::mutex mutex_; // Protects `registry_`.
21
-
22
- RNFWorkletRuntimeRegistry() {} // private ctor
23
-
24
- static void registerRuntime(jsi::Runtime& runtime) {
25
- std::lock_guard<std::mutex> lock(mutex_);
26
- registry_.insert(&runtime);
27
- }
28
-
29
- static void unregisterRuntime(jsi::Runtime& runtime) {
30
- std::lock_guard<std::mutex> lock(mutex_);
31
- registry_.erase(&runtime);
32
- }
33
-
34
- friend class WorkletRuntimeCollector;
35
-
36
- public:
37
- static bool isRuntimeAlive(jsi::Runtime* runtime) {
38
- assert(runtime != nullptr);
39
- std::lock_guard<std::mutex> lock(mutex_);
40
- return registry_.find(runtime) != registry_.end();
41
- }
42
- };
43
-
44
- } // namespace margelo