Skip to content

Commit

Permalink
feat: use spinlocks for selector maps
Browse files Browse the repository at this point in the history
  • Loading branch information
edusperoni authored and NathanWalker committed Nov 30, 2022
1 parent 2610ff1 commit c5a8863
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 15 deletions.
22 changes: 9 additions & 13 deletions NativeScript/runtime/Interop.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1411,22 +1411,18 @@ inline bool isBool() {

SEL Interop::GetSwizzledMethodSelector(SEL selector) {
static robin_hood::unordered_map<SEL, SEL> swizzledMethodSelectorCache;
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);

SEL swizzledMethodSelector = NULL;
static SpinMutex p;
SpinLock lock(p);

try {
swizzledMethodSelector = swizzledMethodSelectorCache.at(selector);
} catch(const std::out_of_range&) {
// ignore...
auto it = swizzledMethodSelectorCache.find(selector);
if (it != swizzledMethodSelectorCache.end()) {
return it->second;
}
SEL swizzledMethodSelector = NULL;

if(!swizzledMethodSelector) {
swizzledMethodSelector = sel_registerName((Constants::SwizzledPrefix + std::string(sel_getName(selector))).c_str());
// save to cache
swizzledMethodSelectorCache.emplace(selector, swizzledMethodSelector);
}
swizzledMethodSelector = sel_registerName((Constants::SwizzledPrefix + std::string(sel_getName(selector))).c_str());
// save to cache
swizzledMethodSelectorCache.emplace(selector, swizzledMethodSelector);

return swizzledMethodSelector;
}
Expand Down
8 changes: 6 additions & 2 deletions NativeScript/runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <set>
#include <vector>
#include "KnownUnknownClassPair.h"
#include "SpinLock.h"

namespace tns {

Expand Down Expand Up @@ -760,8 +761,11 @@ struct MethodMeta : MemberMeta {

inline SEL selector() const {
static robin_hood::unordered_map<const MethodMeta*, SEL> methodMetaSelectorCache;
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
// this method takes a few ns to run and is almost never called by another thread
// this means that locking a mutex is almost always unecessary so we use a spinlock
// that will most likely never spin
static SpinMutex p;
SpinLock lock(p);
SEL ret = nullptr;
auto it = methodMetaSelectorCache.find(this);
if(it != methodMetaSelectorCache.end()) {
Expand Down
56 changes: 56 additions & 0 deletions NativeScript/runtime/SpinLock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef SpinLock_h
#define SpinLock_h


/**
WARNING:
Do NOT use this.
More ofthen than not a normal mutex is better and this spinlock is really unfair in multi threading
This is only supposed to be used in places where the function is very fast and the expected concurrency is very low
If any of those things are false, this WILL be slower and worse than a mutex or a read write mutex
The only place this is currently used is for caching selectors, which take ns to run and are not locked to a specific isolate.
*/

struct SpinMutex {
std::atomic<bool> lock_ = {0};

inline void lock() noexcept {
for (;;) {
// Optimistically assume the lock is free on the first try
if (!lock_.exchange(true, std::memory_order_acquire)) {
return;
}
// Wait for lock to be released without generating cache misses
while (lock_.load(std::memory_order_relaxed)) {
// Issue X86 PAUSE or ARM YIELD instruction to reduce contention between
// hyper-threads
//__builtin_ia32_pause();
}
}
}

bool try_lock() noexcept {
// First do a relaxed load to check if lock is free in order to prevent
// unnecessary cache misses if someone does while(!try_lock())
return !lock_.load(std::memory_order_relaxed) &&
!lock_.exchange(true, std::memory_order_acquire);
}

inline void unlock() noexcept {
lock_.store(false, std::memory_order_release);
}
};

struct SpinLock {
SpinMutex& _mutex;
SpinLock(SpinMutex& m) : _mutex(m) {
_mutex.lock();
}
~SpinLock() {
_mutex.unlock();
}
};


#endif /* SpinLock_h */
4 changes: 4 additions & 0 deletions v8ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
2B7EA6AF2353477000E5184E /* NativeScriptException.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B7EA6AD2353476F00E5184E /* NativeScriptException.mm */; };
2B7EA6B02353477000E5184E /* NativeScriptException.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B7EA6AE2353477000E5184E /* NativeScriptException.h */; };
3CEF9CCD28F896BC0056BA45 /* SpinLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CEF9CCC28F896B70056BA45 /* SpinLock.h */; };
C205257F2577D6F900C12A5C /* NativeScript.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2DDEB32229EAB3B00345BFE /* NativeScript.framework */; };
C20525802577D6F900C12A5C /* NativeScript.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C2DDEB32229EAB3B00345BFE /* NativeScript.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C20525A82577D86600C12A5C /* TNSWidgets.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C20525A72577D86600C12A5C /* TNSWidgets.xcframework */; };
Expand Down Expand Up @@ -541,6 +542,7 @@
/* Begin PBXFileReference section */
2B7EA6AD2353476F00E5184E /* NativeScriptException.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeScriptException.mm; sourceTree = "<group>"; };
2B7EA6AE2353477000E5184E /* NativeScriptException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeScriptException.h; sourceTree = "<group>"; };
3CEF9CCC28F896B70056BA45 /* SpinLock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpinLock.h; sourceTree = "<group>"; };
C2003F9E23FA78CD0043B815 /* TNSDerivedClass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TNSDerivedClass.h; sourceTree = "<group>"; };
C20525A72577D86600C12A5C /* TNSWidgets.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = TNSWidgets.xcframework; sourceTree = "<group>"; };
C20AB5E426E1015200E2B41D /* OneByteStringResource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OneByteStringResource.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1933,6 +1935,7 @@
C2D215FE248AAAD900EDC646 /* Constants.cpp */,
C275F476253B37AB00A997D5 /* UnmanagedType.h */,
C275F475253B37AB00A997D5 /* UnmanagedType.mm */,
3CEF9CCC28F896B70056BA45 /* SpinLock.h */,
);
path = runtime;
sourceTree = "<group>";
Expand Down Expand Up @@ -2065,6 +2068,7 @@
C247C3AA22F828E3001D2CA2 /* once.h in Headers */,
C247C3A822F828E3001D2CA2 /* lazy-instance.h in Headers */,
C27E5D8622F2FDDB00498ED0 /* KnownUnknownClassPair.h in Headers */,
3CEF9CCD28F896BC0056BA45 /* SpinLock.h in Headers */,
C247C36922F828E3001D2CA2 /* Security.h in Headers */,
C247C33822F828E3001D2CA2 /* v8.h in Headers */,
C247C3A722F828E3001D2CA2 /* atomicops.h in Headers */,
Expand Down

0 comments on commit c5a8863

Please sign in to comment.