mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-03-28 23:58:50 +08:00
[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:
committed by
Adlai Holler
parent
d9db780b0b
commit
aba05a747c
@@ -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
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user