mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
316 lines
8.2 KiB
Objective-C
316 lines
8.2 KiB
Objective-C
//
|
|
// ASThread.h
|
|
// Texture
|
|
//
|
|
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
|
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
|
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#import <pthread.h>
|
|
|
|
#import <AsyncDisplayKit/ASAssert.h>
|
|
#import <AsyncDisplayKit/ASAvailability.h>
|
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
|
#import <AsyncDisplayKit/ASConfigurationInternal.h>
|
|
#import <AsyncDisplayKit/ASRecursiveUnfairLock.h>
|
|
|
|
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASDisplayNodeThreadIsMain()
|
|
{
|
|
return 0 != pthread_main_np();
|
|
}
|
|
|
|
/**
|
|
* Adds the lock to the current scope.
|
|
*
|
|
* A C version of the C++ lockers. Pass in any id<NSLocking>.
|
|
* One benefit this has over C++ lockers is that the lock is retained. We
|
|
* had bugs in the past where an object would be deallocated while someone
|
|
* had locked its instanceLock, and we'd get a crash. This macro
|
|
* retains the locked object until it can be unlocked, which is nice.
|
|
*/
|
|
#define ASLockScope(nsLocking) \
|
|
id<NSLocking> __lockToken __attribute__((cleanup(_ASLockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \
|
|
[__lockToken lock];
|
|
|
|
/// Same as ASLockScope(1) but lock isn't retained (be careful).
|
|
#define ASLockScopeUnowned(nsLocking) \
|
|
__unsafe_unretained id<NSLocking> __lockToken __attribute__((cleanup(_ASLockScopeUnownedCleanup))) = nsLocking; \
|
|
[__lockToken lock];
|
|
|
|
ASDISPLAYNODE_INLINE void _ASLockScopeCleanup(id<NSLocking> __strong * const lockPtr) {
|
|
[*lockPtr unlock];
|
|
}
|
|
|
|
ASDISPLAYNODE_INLINE void _ASLockScopeUnownedCleanup(id<NSLocking> __unsafe_unretained * const lockPtr) {
|
|
[*lockPtr unlock];
|
|
}
|
|
|
|
/**
|
|
* Same as ASLockScope(1) but it uses self, so we can skip retain/release.
|
|
*/
|
|
#define ASLockScopeSelf() ASLockScopeUnowned(self)
|
|
|
|
/// One-liner while holding the lock.
|
|
#define ASLocked(nsLocking, expr) ({ ASLockScope(nsLocking); expr; })
|
|
|
|
/// Faster self-version.
|
|
#define ASLockedSelf(expr) ({ ASLockScopeSelf(); expr; })
|
|
|
|
#define ASLockedSelfCompareAssign(lvalue, newValue) \
|
|
ASLockedSelf(ASCompareAssign(lvalue, newValue))
|
|
|
|
#define ASLockedSelfCompareAssignObjects(lvalue, newValue) \
|
|
ASLockedSelf(ASCompareAssignObjects(lvalue, newValue))
|
|
|
|
#define ASLockedSelfCompareAssignCustom(lvalue, newValue, isequal) \
|
|
ASLockedSelf(ASCompareAssignCustom(lvalue, newValue, isequal))
|
|
|
|
#define ASLockedSelfCompareAssignCopy(lvalue, obj) \
|
|
ASLockedSelf(ASCompareAssignCopy(lvalue, obj))
|
|
|
|
#define ASUnlockScope(nsLocking) \
|
|
id<NSLocking> __lockToken __attribute__((cleanup(_ASUnlockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \
|
|
[__lockToken unlock];
|
|
|
|
#define ASSynthesizeLockingMethodsWithMutex(mutex) \
|
|
- (void)lock { mutex.lock(); } \
|
|
- (void)unlock { mutex.unlock(); } \
|
|
- (BOOL)tryLock { return (BOOL)mutex.try_lock(); }
|
|
|
|
#define ASSynthesizeLockingMethodsWithObject(object) \
|
|
- (void)lock { [object lock]; } \
|
|
- (void)unlock { [object unlock]; } \
|
|
- (BOOL)tryLock { return [object tryLock]; }
|
|
|
|
ASDISPLAYNODE_INLINE void _ASUnlockScopeCleanup(id<NSLocking> __strong *lockPtr) {
|
|
[*lockPtr lock];
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <new>
|
|
#include <thread>
|
|
|
|
// These macros are here for legacy reasons. We may get rid of them later.
|
|
#define ASAssertLocked(m) m.AssertHeld()
|
|
#define ASAssertUnlocked(m) m.AssertNotHeld()
|
|
|
|
namespace AS {
|
|
|
|
// Set once in Mutex constructor. Linker fails if this is a member variable. ??
|
|
static bool gMutex_unfair;
|
|
|
|
// Silence unguarded availability warnings in here, because
|
|
// perf is critical and we will check availability once
|
|
// and not again.
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
|
class Mutex
|
|
{
|
|
public:
|
|
/// Constructs a plain mutex (the default).
|
|
Mutex () : Mutex (false) {}
|
|
|
|
~Mutex () {
|
|
// Manually destroy since unions can't do it.
|
|
switch (_type) {
|
|
case Plain:
|
|
_plain.~mutex();
|
|
break;
|
|
case Recursive:
|
|
_recursive.~recursive_mutex();
|
|
break;
|
|
case Unfair:
|
|
// nop
|
|
break;
|
|
case RecursiveUnfair:
|
|
// nop
|
|
break;
|
|
}
|
|
}
|
|
|
|
Mutex (const Mutex&) = delete;
|
|
Mutex &operator=(const Mutex&) = delete;
|
|
|
|
bool try_lock() {
|
|
bool success = false;
|
|
switch (_type) {
|
|
case Plain:
|
|
success = _plain.try_lock();
|
|
break;
|
|
case Recursive:
|
|
success = _recursive.try_lock();
|
|
break;
|
|
case Unfair:
|
|
#if AS_USE_OS_LOCK
|
|
success = os_unfair_lock_trylock(&_unfair);
|
|
#else
|
|
success = OSSpinLockTry(&_unfair);
|
|
#endif
|
|
break;
|
|
case RecursiveUnfair:
|
|
success = ASRecursiveUnfairLockTryLock(&_runfair);
|
|
break;
|
|
}
|
|
if (success) {
|
|
DidLock();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void lock() {
|
|
switch (_type) {
|
|
case Plain:
|
|
_plain.lock();
|
|
break;
|
|
case Recursive:
|
|
_recursive.lock();
|
|
break;
|
|
case Unfair:
|
|
#if AS_USE_OS_LOCK
|
|
os_unfair_lock_lock(&_unfair);
|
|
#else
|
|
OSSpinLockLock(&_unfair);
|
|
#endif
|
|
break;
|
|
case RecursiveUnfair:
|
|
ASRecursiveUnfairLockLock(&_runfair);
|
|
break;
|
|
}
|
|
DidLock();
|
|
}
|
|
|
|
void unlock() {
|
|
WillUnlock();
|
|
switch (_type) {
|
|
case Plain:
|
|
_plain.unlock();
|
|
break;
|
|
case Recursive:
|
|
_recursive.unlock();
|
|
break;
|
|
case Unfair:
|
|
#if AS_USE_OS_LOCK
|
|
os_unfair_lock_unlock(&_unfair);
|
|
#else
|
|
OSSpinLockUnlock(&_unfair);
|
|
#endif
|
|
break;
|
|
case RecursiveUnfair:
|
|
ASRecursiveUnfairLockUnlock(&_runfair);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AssertHeld() {
|
|
ASDisplayNodeCAssert(_owner == std::this_thread::get_id(), @"Thread should hold lock");
|
|
}
|
|
|
|
void AssertNotHeld() {
|
|
ASDisplayNodeCAssert(_owner != std::this_thread::get_id(), @"Thread should not hold lock");
|
|
}
|
|
|
|
explicit Mutex (bool recursive) {
|
|
|
|
// Check if we can use unfair lock and store in static var.
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
if (AS_AVAILABLE_IOS_TVOS(10, 10)) {
|
|
gMutex_unfair = ASActivateExperimentalFeature(ASExperimentalUnfairLock);
|
|
}
|
|
gMutex_unfair = true;
|
|
});
|
|
|
|
if (recursive) {
|
|
if (gMutex_unfair) {
|
|
_type = RecursiveUnfair;
|
|
_runfair = AS_RECURSIVE_UNFAIR_LOCK_INIT;
|
|
} else {
|
|
_type = Recursive;
|
|
new (&_recursive) std::recursive_mutex();
|
|
}
|
|
} else {
|
|
if (gMutex_unfair) {
|
|
_type = Unfair;
|
|
#if AS_USE_OS_LOCK
|
|
_unfair = OS_UNFAIR_LOCK_INIT;
|
|
#else
|
|
_unfair = OS_SPINLOCK_INIT;
|
|
#endif
|
|
} else {
|
|
_type = Plain;
|
|
new (&_plain) std::mutex();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
enum Type {
|
|
Plain,
|
|
Recursive,
|
|
Unfair,
|
|
RecursiveUnfair
|
|
};
|
|
|
|
void WillUnlock() {
|
|
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
|
|
if (--_count == 0) {
|
|
_owner = std::thread::id();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DidLock() {
|
|
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
|
|
if (++_count == 1) {
|
|
// New owner.
|
|
_owner = std::this_thread::get_id();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Type _type;
|
|
union {
|
|
#if AS_USE_OS_LOCK
|
|
os_unfair_lock _unfair;
|
|
#else
|
|
OSSpinLock _unfair;
|
|
#endif
|
|
ASRecursiveUnfairLock _runfair;
|
|
std::mutex _plain;
|
|
std::recursive_mutex _recursive;
|
|
};
|
|
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
|
|
std::thread::id _owner = std::thread::id();
|
|
int _count = 0;
|
|
#endif
|
|
};
|
|
#pragma clang diagnostic pop // ignored "-Wunguarded-availability"
|
|
|
|
/**
|
|
Obj-C doesn't allow you to pass parameters to C++ ivar constructors.
|
|
Provide a convenience to change the default from non-recursive to recursive.
|
|
|
|
But wait! Recursive mutexes are a bad idea. Think twice before using one:
|
|
|
|
http://www.zaval.org/resources/library/butenhof1.html
|
|
http://www.fieryrobot.com/blog/2008/10/14/recursive-locks-will-kill-you/
|
|
*/
|
|
class RecursiveMutex : public Mutex
|
|
{
|
|
public:
|
|
RecursiveMutex () : Mutex (true) {}
|
|
};
|
|
|
|
typedef std::lock_guard<Mutex> MutexLocker;
|
|
typedef std::unique_lock<Mutex> UniqueLock;
|
|
|
|
} // namespace AS
|
|
|
|
#endif /* __cplusplus */
|