[ASThread] Add SharedLocker and SharedUnlocker that uses a shared pointer for the mutex (#2047)

* Add SharedLocker and SharedUnlocker that uses a shared pointer for the mutex

* Move ASTextKitContext to use Shared Locker
This commit is contained in:
Michael Schneider
2016-08-09 15:20:31 -07:00
committed by Adlai Holler
parent d9db780b0b
commit aba05a747c
3 changed files with 86 additions and 19 deletions

View File

@@ -34,6 +34,8 @@ static inline BOOL ASDisplayNodeThreadIsMain()
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
#endif #endif
#include <memory>
/** /**
For use with ASDN::StaticMutex only. For use with ASDN::StaticMutex only.
*/ */
@@ -53,7 +55,7 @@ static inline BOOL ASDisplayNodeThreadIsMain()
namespace ASDN { namespace ASDN {
template<class T> template<class T>
class Locker class Locker
{ {
@@ -98,17 +100,72 @@ namespace ASDN {
}; };
template<class T>
class SharedLocker
{
std::shared_ptr<T> _l;
#if TIME_LOCKER
CFTimeInterval _ti;
const char *_name;
#endif
public:
#if !TIME_LOCKER
SharedLocker (std::shared_ptr<T> const& l) ASDISPLAYNODE_NOTHROW : _l (l) {
assert(_l != nullptr);
_l->lock ();
}
~SharedLocker () {
_l->unlock ();
}
// non-copyable.
SharedLocker(const SharedLocker<T>&) = delete;
SharedLocker &operator=(const SharedLocker<T>&) = delete;
#else
SharedLocker (std::shared_ptr<T> const& l, const char *name = NULL) ASDISPLAYNODE_NOTHROW : _l (l), _name(name) {
_ti = CACurrentMediaTime();
_l->lock ();
}
~SharedLocker () {
_l->unlock ();
if (_name) {
printf(_name, NULL);
printf(" dt:%f\n", CACurrentMediaTime() - _ti);
}
}
#endif
};
template<class T> template<class T>
class Unlocker class Unlocker
{ {
T &_l; T &_l;
public: public:
Unlocker (T &l) ASDISPLAYNODE_NOTHROW : _l (l) {_l.unlock ();} Unlocker (T &l) ASDISPLAYNODE_NOTHROW : _l (l) { _l.unlock (); }
~Unlocker () {_l.lock ();} ~Unlocker () {_l.lock ();}
Unlocker(Unlocker<T>&) = delete; Unlocker(Unlocker<T>&) = delete;
Unlocker &operator=(Unlocker<T>&) = delete; Unlocker &operator=(Unlocker<T>&) = delete;
}; };
template<class T>
class SharedUnlocker
{
std::shared_ptr<T> _l;
public:
SharedUnlocker (std::shared_ptr<T> const& l) ASDISPLAYNODE_NOTHROW : _l (l) { _l->unlock (); }
~SharedUnlocker () { _l->lock (); }
SharedUnlocker(SharedUnlocker<T>&) = delete;
SharedUnlocker &operator=(SharedUnlocker<T>&) = delete;
};
struct Mutex struct Mutex
{ {
@@ -164,7 +221,9 @@ namespace ASDN {
}; };
typedef Locker<Mutex> MutexLocker; typedef Locker<Mutex> MutexLocker;
typedef SharedLocker<Mutex> MutexSharedLocker;
typedef Unlocker<Mutex> MutexUnlocker; typedef Unlocker<Mutex> MutexUnlocker;
typedef SharedUnlocker<Mutex> MutexSharedUnlocker;
/** /**
If you are creating a static mutex, use StaticMutex and specify its default value as one of ASDISPLAYNODE_MUTEX_INITIALIZER If you are creating a static mutex, use StaticMutex and specify its default value as one of ASDISPLAYNODE_MUTEX_INITIALIZER

View File

@@ -16,6 +16,7 @@
#import "ASLayout.h" #import "ASLayout.h"
#import <queue> #import <queue>
#import <memory>
#import "NSArray+Diffing.h" #import "NSArray+Diffing.h"
#import "ASEqualityHelpers.h" #import "ASEqualityHelpers.h"
@@ -47,7 +48,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
} }
@implementation ASLayoutTransition { @implementation ASLayoutTransition {
ASDN::RecursiveMutex __instanceLock__; std::shared_ptr<ASDN::RecursiveMutex> __instanceLock__;
BOOL _calculatedSubnodeOperations; BOOL _calculatedSubnodeOperations;
NSArray<ASDisplayNode *> *_insertedSubnodes; NSArray<ASDisplayNode *> *_insertedSubnodes;
NSArray<ASDisplayNode *> *_removedSubnodes; NSArray<ASDisplayNode *> *_removedSubnodes;
@@ -61,6 +63,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
__instanceLock__ = std::make_shared<ASDN::RecursiveMutex>();
_node = node; _node = node;
_pendingLayout = pendingLayout; _pendingLayout = pendingLayout;
_previousLayout = previousLayout; _previousLayout = previousLayout;
@@ -70,7 +74,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (BOOL)isSynchronous - (BOOL)isSynchronous
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
return !ASLayoutCanTransitionAsynchronous(_pendingLayout); return !ASLayoutCanTransitionAsynchronous(_pendingLayout);
} }
@@ -82,7 +86,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (void)applySubnodeInsertions - (void)applySubnodeInsertions
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
[self calculateSubnodeOperationsIfNeeded]; [self calculateSubnodeOperationsIfNeeded];
NSUInteger i = 0; NSUInteger i = 0;
@@ -95,7 +99,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (void)applySubnodeRemovals - (void)applySubnodeRemovals
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
[self calculateSubnodeOperationsIfNeeded]; [self calculateSubnodeOperationsIfNeeded];
for (ASDisplayNode *subnode in _removedSubnodes) { for (ASDisplayNode *subnode in _removedSubnodes) {
[subnode removeFromSupernode]; [subnode removeFromSupernode];
@@ -104,7 +108,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (void)calculateSubnodeOperationsIfNeeded - (void)calculateSubnodeOperationsIfNeeded
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
if (_calculatedSubnodeOperations) { if (_calculatedSubnodeOperations) {
return; return;
} }
@@ -134,27 +138,27 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (NSArray<ASDisplayNode *> *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context - (NSArray<ASDisplayNode *> *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
return _node.subnodes; return _node.subnodes;
} }
- (NSArray<ASDisplayNode *> *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context - (NSArray<ASDisplayNode *> *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
[self calculateSubnodeOperationsIfNeeded]; [self calculateSubnodeOperationsIfNeeded];
return _insertedSubnodes; return _insertedSubnodes;
} }
- (NSArray<ASDisplayNode *> *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context - (NSArray<ASDisplayNode *> *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
[self calculateSubnodeOperationsIfNeeded]; [self calculateSubnodeOperationsIfNeeded];
return _removedSubnodes; return _removedSubnodes;
} }
- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key - (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
return _previousLayout; return _previousLayout;
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
@@ -166,7 +170,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key - (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
return _previousLayout.constrainedSizeRange; return _previousLayout.constrainedSizeRange;
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {

View File

@@ -10,13 +10,14 @@
#import "ASTextKitContext.h" #import "ASTextKitContext.h"
#import "ASLayoutManager.h" #import "ASLayoutManager.h"
#import "ASThread.h"
#import <mutex> #include <memory>
@implementation ASTextKitContext @implementation ASTextKitContext
{ {
// All TextKit operations (even non-mutative ones) must be executed serially. // All TextKit operations (even non-mutative ones) must be executed serially.
std::mutex _textKitMutex; std::shared_ptr<ASDN::Mutex> __instanceLock__;
NSLayoutManager *_layoutManager; NSLayoutManager *_layoutManager;
NSTextStorage *_textStorage; NSTextStorage *_textStorage;
@@ -35,8 +36,11 @@
{ {
if (self = [super init]) { if (self = [super init]) {
// Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock. // Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock.
static std::mutex __static_mutex; static ASDN::Mutex __staticMutex;
std::lock_guard<std::mutex> l(__static_mutex); ASDN::MutexLocker l(__staticMutex);
__instanceLock__ = std::make_shared<ASDN::Mutex>();
// Create the TextKit component stack with our default configuration. // Create the TextKit component stack with our default configuration.
if (textStorageCreationBlock) { if (textStorageCreationBlock) {
_textStorage = textStorageCreationBlock(attributedString); _textStorage = textStorageCreationBlock(attributedString);
@@ -60,13 +64,13 @@
- (CGSize)constrainedSize - (CGSize)constrainedSize
{ {
std::lock_guard<std::mutex> l(_textKitMutex); ASDN::MutexSharedLocker l(__instanceLock__);
return _textContainer.size; return _textContainer.size;
} }
- (void)setConstrainedSize:(CGSize)constrainedSize - (void)setConstrainedSize:(CGSize)constrainedSize
{ {
std::lock_guard<std::mutex> l(_textKitMutex); ASDN::MutexSharedLocker l(__instanceLock__);
_textContainer.size = constrainedSize; _textContainer.size = constrainedSize;
} }
@@ -74,7 +78,7 @@
NSTextStorage *, NSTextStorage *,
NSTextContainer *))block NSTextContainer *))block
{ {
std::lock_guard<std::mutex> l(_textKitMutex); ASDN::MutexSharedLocker l(__instanceLock__);
if (block) { if (block) {
block(_layoutManager, _textStorage, _textContainer); block(_layoutManager, _textStorage, _textContainer);
} }