From 9a85f0782ea4647d77515b48b4bd2e1b0826210a Mon Sep 17 00:00:00 2001 From: andersc Date: Wed, 13 Sep 2017 07:31:46 -0700 Subject: [PATCH] Enhance RTCUIApplicationStatusObserver thread safety. Add locking around waiting for initialization to finish, since calling dispatch_block_wait from multiple threads leads to undefined behavior. Initialize RTCUIApplicationStatusObserver earlier to give the initialization block more time to run on the main thread before starting to query the application state. http://www.dailymotion.com/video/x2mckmh BUG=b/65558688 Review-Url: https://codereview.webrtc.org/3009383002 Cr-Commit-Position: refs/heads/master@{#19822} --- .../Common/RTCUIApplicationStatusObserver.h | 1 + .../Common/RTCUIApplicationStatusObserver.m | 23 ++++++++++++++++--- .../VideoToolbox/RTCVideoDecoderH264.mm | 10 ++++++++ .../VideoToolbox/RTCVideoEncoderH264.mm | 4 ++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.h b/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.h index ff38628383..a2064df5db 100644 --- a/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.h +++ b/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.h @@ -15,6 +15,7 @@ @interface RTCUIApplicationStatusObserver : NSObject + (instancetype)sharedInstance; ++ (void)prepareForUse; - (BOOL)isApplicationActive; diff --git a/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.m b/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.m index 1d2aab48e8..7134773610 100644 --- a/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.m +++ b/webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.m @@ -26,6 +26,7 @@ @implementation RTCUIApplicationStatusObserver { BOOL _initialized; dispatch_block_t _initializeBlock; + dispatch_semaphore_t _waitForInitializeSemaphore; UIApplicationState _state; id _activeObserver; @@ -45,6 +46,12 @@ return sharedInstance; } +// Method to make sure observers are added and the initialization block is +// scheduled to run on the main queue. ++ (void)prepareForUse { + __unused RTCUIApplicationStatusObserver *observer = [self sharedInstance]; +} + - (id)init { if (self = [super init]) { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; @@ -65,6 +72,7 @@ [UIApplication sharedApplication].applicationState; }]; + _waitForInitializeSemaphore = dispatch_semaphore_create(1); _initialized = NO; _initializeBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{ weakSelf.state = [UIApplication sharedApplication].applicationState; @@ -84,10 +92,19 @@ } - (BOOL)isApplicationActive { + // NOTE: The function `dispatch_block_wait` can only legally be called once. + // Because of this, if several threads call the `isApplicationActive` method before + // the `_initializeBlock` has been executed, instead of multiple threads calling + // `dispatch_block_wait`, the other threads need to wait for the first waiting thread + // instead. if (!_initialized) { - long ret = dispatch_block_wait(_initializeBlock, - dispatch_time(DISPATCH_TIME_NOW, 10.0 * NSEC_PER_SEC)); - RTC_DCHECK_EQ(ret, 0); + dispatch_semaphore_wait(_waitForInitializeSemaphore, DISPATCH_TIME_FOREVER); + if (!_initialized) { + long ret = dispatch_block_wait(_initializeBlock, + dispatch_time(DISPATCH_TIME_NOW, 10.0 * NSEC_PER_SEC)); + RTC_DCHECK_EQ(ret, 0); + } + dispatch_semaphore_signal(_waitForInitializeSemaphore); } return _state == UIApplicationStateActive; } diff --git a/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm b/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm index f657f9c2f1..b19cb43bd1 100644 --- a/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm +++ b/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm @@ -67,6 +67,16 @@ void decompressionOutputCallback(void *decoder, RTCVideoDecoderCallback _callback; } +- (instancetype)init { + if (self = [super init]) { +#if defined(WEBRTC_IOS) + [RTCUIApplicationStatusObserver prepareForUse]; +#endif + } + + return self; +} + - (void)dealloc { [self destroyDecompressionSession]; [self setVideoFormat:nullptr]; diff --git a/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoEncoderH264.mm b/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoEncoderH264.mm index 0fcefeef02..6e2b568273 100644 --- a/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoEncoderH264.mm +++ b/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoEncoderH264.mm @@ -301,6 +301,10 @@ CFStringRef ExtractProfile(const cricket::VideoCodec &codec) { _profile = ExtractProfile([codecInfo nativeVideoCodec]); LOG(LS_INFO) << "Using profile " << CFStringToString(_profile); RTC_CHECK([codecInfo.name isEqualToString:@"H264"]); + +#if defined(WEBRTC_IOS) + [RTCUIApplicationStatusObserver prepareForUse]; +#endif } return self; }