[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>
#endif
#include <memory>
/**
For use with ASDN::StaticMutex only.
*/
@@ -53,7 +55,7 @@ static inline BOOL ASDisplayNodeThreadIsMain()
namespace ASDN {
template<class T>
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>
class Unlocker
{
T &_l;
public:
Unlocker (T &l) ASDISPLAYNODE_NOTHROW : _l (l) {_l.unlock ();}
Unlocker (T &l) ASDISPLAYNODE_NOTHROW : _l (l) { _l.unlock (); }
~Unlocker () {_l.lock ();}
Unlocker(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
{
@@ -164,7 +221,9 @@ namespace ASDN {
};
typedef Locker<Mutex> MutexLocker;
typedef SharedLocker<Mutex> MutexSharedLocker;
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

View File

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

View File

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