Skip to content

Commit

Permalink
Use reader/writer lock for TurboModule cache access
Browse files Browse the repository at this point in the history
Summary:
Right now, when two threads require two NativeModules, both threads fight for the same `std::mutex`. Why? Because NativeModule require can read from/write to the shared `std::unordered_map<std::string, TurboModuleHolder*>`.

**A Few Thoughts:**
- All threads should be able to read from the TurboModule cache concurrently, without issue. Only writes into the cache require exclusive access. *With our current setup, both reads and writes acquire exclusive access.*
- During the lifetime of an application, there will only be tens of NativeModule create events (i.e: writes into the TurboModule cache). However, there may be hundreds, if not thousands of TurboModule cache lookups. *We don't need to serialize those hundreds/thousands of TurboModule cache reads.*

This is a potential optimization opportunity for the TurboModule infra.

## Changes
This diff introduces a `std::shared_mutex` inside `RCTTurboModuleManager`. We use either the regular `std::mutex` or the `std::shared_mutex`, depending on whether `RCTTurboModuleSharedMutexInitEnabled()` is `YES`.

Changelog: [Internal]

Reviewed By: fkgozali

Differential Revision: D23413118

fbshipit-source-id: 0880413c691b141db98a2715648f0c3e05983307
  • Loading branch information
RSNara authored and facebook-github-bot committed Sep 9, 2020
1 parent 8933724 commit f5c246d
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 12 deletions.
4 changes: 4 additions & 0 deletions React/Base/RCTBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ RCT_EXTERN void RCTEnableTurboModule(BOOL enabled);
RCT_EXTERN BOOL RCTTurboModuleEagerInitEnabled(void);
RCT_EXTERN void RCTEnableTurboModuleEagerInit(BOOL enabled);

// Turn on TurboModule shared mutex initialization
RCT_EXTERN BOOL RCTTurboModuleSharedMutexInitEnabled(void);
RCT_EXTERN void RCTEnableTurboModuleSharedMutexInit(BOOL enabled);

/**
* Async batched bridge used to communicate with the JavaScript application.
*/
Expand Down
11 changes: 11 additions & 0 deletions React/Base/RCTBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ void RCTEnableTurboModuleEagerInit(BOOL enabled)
turboModuleEagerInitEnabled = enabled;
}

static BOOL turboModuleSharedMutexInitEnabled = NO;
BOOL RCTTurboModuleSharedMutexInitEnabled(void)
{
return turboModuleSharedMutexInitEnabled;
}

void RCTEnableTurboModuleSharedMutexInit(BOOL enabled)
{
turboModuleSharedMutexInitEnabled = enabled;
}

@interface RCTBridge () <RCTReloadListener>
@end

Expand Down
60 changes: 48 additions & 12 deletions ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <cassert>
#import <condition_variable>
#import <mutex>
#import <shared_mutex>

#import <objc/runtime.h>

Expand Down Expand Up @@ -168,6 +169,7 @@ @implementation RCTTurboModuleManager {
std::mutex _turboModuleManagerDelegateMutex;

// Enforce synchronous access to _invalidating and _turboModuleHolders
std::shared_timed_mutex _turboModuleHoldersSharedMutex;
std::mutex _turboModuleHoldersMutex;
std::atomic<bool> _invalidating;
}
Expand Down Expand Up @@ -314,6 +316,33 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name
return turboModule;
}

- (TurboModuleHolder *)_getOrCreateTurboModuleHolder:(const char *)moduleName
{
if (RCTTurboModuleSharedMutexInitEnabled()) {
{
std::shared_lock<std::shared_timed_mutex> guard(_turboModuleHoldersSharedMutex);
if (_invalidating) {
return nullptr;
}

auto it = _turboModuleHolders.find(moduleName);
if (it != _turboModuleHolders.end()) {
return &it->second;
}
}

std::unique_lock<std::shared_timed_mutex> guard(_turboModuleHoldersSharedMutex);
return &_turboModuleHolders[moduleName];
}

std::lock_guard<std::mutex> guard(_turboModuleHoldersMutex);
if (_invalidating) {
return nullptr;
}

return &_turboModuleHolders[moduleName];
}

/**
* Given a name for a TurboModule, return an ObjC object which is the instance
* of that TurboModule ObjC class. If no TurboModule exist with the provided name,
Expand All @@ -324,15 +353,10 @@ - (void)notifyAboutTurboModuleSetup:(const char *)name
*/
- (id<RCTTurboModule>)provideRCTTurboModule:(const char *)moduleName
{
TurboModuleHolder *moduleHolder;
TurboModuleHolder *moduleHolder = [self _getOrCreateTurboModuleHolder:moduleName];

{
std::lock_guard<std::mutex> guard(_turboModuleHoldersMutex);
if (_invalidating) {
return nil;
}

moduleHolder = &_turboModuleHolders[moduleName];
if (!moduleHolder) {
return nil;
}

TurboModulePerfLogger::moduleCreateStart(moduleName, moduleHolder->getModuleId());
Expand Down Expand Up @@ -719,6 +743,11 @@ - (id)moduleForName:(const char *)moduleName warnOnLookupFailure:(BOOL)warnOnLoo

- (BOOL)moduleIsInitialized:(const char *)moduleName
{
if (RCTTurboModuleSharedMutexInitEnabled()) {
std::shared_lock<std::shared_timed_mutex> guard(_turboModuleHoldersSharedMutex);
return _turboModuleHolders.find(moduleName) != _turboModuleHolders.end();
}

std::unique_lock<std::mutex> guard(_turboModuleHoldersMutex);
return _turboModuleHolders.find(moduleName) != _turboModuleHolders.end();
}
Expand Down Expand Up @@ -750,10 +779,14 @@ - (void)bridgeWillInvalidateModules:(NSNotification *)notification
return;
}

std::lock_guard<std::mutex> guard(_turboModuleHoldersMutex);

// This should halt all insertions into _turboModuleHolders
_invalidating = true;
if (RCTTurboModuleSharedMutexInitEnabled()) {
std::unique_lock<std::shared_timed_mutex> guard(_turboModuleHoldersSharedMutex);
_invalidating = true;
} else {
std::lock_guard<std::mutex> guard(_turboModuleHoldersMutex);
_invalidating = true;
}
}

- (void)bridgeDidInvalidateModules:(NSNotification *)notification
Expand Down Expand Up @@ -807,7 +840,10 @@ - (void)bridgeDidInvalidateModules:(NSNotification *)notification

- (void)invalidate
{
{
if (RCTTurboModuleSharedMutexInitEnabled()) {
std::unique_lock<std::shared_timed_mutex> guard(_turboModuleHoldersSharedMutex);
_invalidating = true;
} else {
std::lock_guard<std::mutex> guard(_turboModuleHoldersMutex);
_invalidating = true;
}
Expand Down

0 comments on commit f5c246d

Please sign in to comment.