Skip to content

Commit c5a8863

Browse files
edusperoniNathanWalker
authored andcommitted
feat: use spinlocks for selector maps
1 parent 2610ff1 commit c5a8863

File tree

4 files changed

+75
-15
lines changed

4 files changed

+75
-15
lines changed

NativeScript/runtime/Interop.mm

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,22 +1411,18 @@ inline bool isBool() {
14111411

14121412
SEL Interop::GetSwizzledMethodSelector(SEL selector) {
14131413
static robin_hood::unordered_map<SEL, SEL> swizzledMethodSelectorCache;
1414-
static std::mutex mutex;
1415-
std::lock_guard<std::mutex> lock(mutex);
1416-
1417-
SEL swizzledMethodSelector = NULL;
1414+
static SpinMutex p;
1415+
SpinLock lock(p);
14181416

1419-
try {
1420-
swizzledMethodSelector = swizzledMethodSelectorCache.at(selector);
1421-
} catch(const std::out_of_range&) {
1422-
// ignore...
1417+
auto it = swizzledMethodSelectorCache.find(selector);
1418+
if (it != swizzledMethodSelectorCache.end()) {
1419+
return it->second;
14231420
}
1421+
SEL swizzledMethodSelector = NULL;
14241422

1425-
if(!swizzledMethodSelector) {
1426-
swizzledMethodSelector = sel_registerName((Constants::SwizzledPrefix + std::string(sel_getName(selector))).c_str());
1427-
// save to cache
1428-
swizzledMethodSelectorCache.emplace(selector, swizzledMethodSelector);
1429-
}
1423+
swizzledMethodSelector = sel_registerName((Constants::SwizzledPrefix + std::string(sel_getName(selector))).c_str());
1424+
// save to cache
1425+
swizzledMethodSelectorCache.emplace(selector, swizzledMethodSelector);
14301426

14311427
return swizzledMethodSelector;
14321428
}

NativeScript/runtime/Metadata.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <set>
1212
#include <vector>
1313
#include "KnownUnknownClassPair.h"
14+
#include "SpinLock.h"
1415

1516
namespace tns {
1617

@@ -760,8 +761,11 @@ struct MethodMeta : MemberMeta {
760761

761762
inline SEL selector() const {
762763
static robin_hood::unordered_map<const MethodMeta*, SEL> methodMetaSelectorCache;
763-
static std::mutex mutex;
764-
std::lock_guard<std::mutex> lock(mutex);
764+
// this method takes a few ns to run and is almost never called by another thread
765+
// this means that locking a mutex is almost always unecessary so we use a spinlock
766+
// that will most likely never spin
767+
static SpinMutex p;
768+
SpinLock lock(p);
765769
SEL ret = nullptr;
766770
auto it = methodMetaSelectorCache.find(this);
767771
if(it != methodMetaSelectorCache.end()) {

NativeScript/runtime/SpinLock.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef SpinLock_h
2+
#define SpinLock_h
3+
4+
5+
/**
6+
WARNING:
7+
Do NOT use this.
8+
More ofthen than not a normal mutex is better and this spinlock is really unfair in multi threading
9+
This is only supposed to be used in places where the function is very fast and the expected concurrency is very low
10+
If any of those things are false, this WILL be slower and worse than a mutex or a read write mutex
11+
12+
The only place this is currently used is for caching selectors, which take ns to run and are not locked to a specific isolate.
13+
*/
14+
15+
struct SpinMutex {
16+
std::atomic<bool> lock_ = {0};
17+
18+
inline void lock() noexcept {
19+
for (;;) {
20+
// Optimistically assume the lock is free on the first try
21+
if (!lock_.exchange(true, std::memory_order_acquire)) {
22+
return;
23+
}
24+
// Wait for lock to be released without generating cache misses
25+
while (lock_.load(std::memory_order_relaxed)) {
26+
// Issue X86 PAUSE or ARM YIELD instruction to reduce contention between
27+
// hyper-threads
28+
//__builtin_ia32_pause();
29+
}
30+
}
31+
}
32+
33+
bool try_lock() noexcept {
34+
// First do a relaxed load to check if lock is free in order to prevent
35+
// unnecessary cache misses if someone does while(!try_lock())
36+
return !lock_.load(std::memory_order_relaxed) &&
37+
!lock_.exchange(true, std::memory_order_acquire);
38+
}
39+
40+
inline void unlock() noexcept {
41+
lock_.store(false, std::memory_order_release);
42+
}
43+
};
44+
45+
struct SpinLock {
46+
SpinMutex& _mutex;
47+
SpinLock(SpinMutex& m) : _mutex(m) {
48+
_mutex.lock();
49+
}
50+
~SpinLock() {
51+
_mutex.unlock();
52+
}
53+
};
54+
55+
56+
#endif /* SpinLock_h */

v8ios.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
2B7EA6AF2353477000E5184E /* NativeScriptException.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B7EA6AD2353476F00E5184E /* NativeScriptException.mm */; };
1111
2B7EA6B02353477000E5184E /* NativeScriptException.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B7EA6AE2353477000E5184E /* NativeScriptException.h */; };
12+
3CEF9CCD28F896BC0056BA45 /* SpinLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CEF9CCC28F896B70056BA45 /* SpinLock.h */; };
1213
C205257F2577D6F900C12A5C /* NativeScript.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2DDEB32229EAB3B00345BFE /* NativeScript.framework */; };
1314
C20525802577D6F900C12A5C /* NativeScript.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C2DDEB32229EAB3B00345BFE /* NativeScript.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1415
C20525A82577D86600C12A5C /* TNSWidgets.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C20525A72577D86600C12A5C /* TNSWidgets.xcframework */; };
@@ -541,6 +542,7 @@
541542
/* Begin PBXFileReference section */
542543
2B7EA6AD2353476F00E5184E /* NativeScriptException.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeScriptException.mm; sourceTree = "<group>"; };
543544
2B7EA6AE2353477000E5184E /* NativeScriptException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeScriptException.h; sourceTree = "<group>"; };
545+
3CEF9CCC28F896B70056BA45 /* SpinLock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpinLock.h; sourceTree = "<group>"; };
544546
C2003F9E23FA78CD0043B815 /* TNSDerivedClass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TNSDerivedClass.h; sourceTree = "<group>"; };
545547
C20525A72577D86600C12A5C /* TNSWidgets.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = TNSWidgets.xcframework; sourceTree = "<group>"; };
546548
C20AB5E426E1015200E2B41D /* OneByteStringResource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OneByteStringResource.cpp; sourceTree = "<group>"; };
@@ -1933,6 +1935,7 @@
19331935
C2D215FE248AAAD900EDC646 /* Constants.cpp */,
19341936
C275F476253B37AB00A997D5 /* UnmanagedType.h */,
19351937
C275F475253B37AB00A997D5 /* UnmanagedType.mm */,
1938+
3CEF9CCC28F896B70056BA45 /* SpinLock.h */,
19361939
);
19371940
path = runtime;
19381941
sourceTree = "<group>";
@@ -2065,6 +2068,7 @@
20652068
C247C3AA22F828E3001D2CA2 /* once.h in Headers */,
20662069
C247C3A822F828E3001D2CA2 /* lazy-instance.h in Headers */,
20672070
C27E5D8622F2FDDB00498ED0 /* KnownUnknownClassPair.h in Headers */,
2071+
3CEF9CCD28F896BC0056BA45 /* SpinLock.h in Headers */,
20682072
C247C36922F828E3001D2CA2 /* Security.h in Headers */,
20692073
C247C33822F828E3001D2CA2 /* v8.h in Headers */,
20702074
C247C3A722F828E3001D2CA2 /* atomicops.h in Headers */,

0 commit comments

Comments
 (0)