From a7468a4acfad40f4af459b7d6aecf32c1107dfbe Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 15 Aug 2017 14:05:22 -0700 Subject: [PATCH] Introducing smart `RCTUnsafeExecuteOnMainQueueOnceSync()` and fixed `RCTScreenScale()` Summary: (This diff was decoupled from D4983054 which landing was quite delayed.) `RCTUnsafeExecuteOnMainQueueOnceSync()` synchronously executes a block once, on main queue. I found that our old implementation of `RCTScreenScale()` causes deadlock when it is called from main and background thread simultaneously. After I implemented my own solution I googled this issue and found an article by Ben Alpert with this awesome helper: https://benalpert.com/2014/04/02/dispatch-once-initialization-on-the-main-thread.html So, I found it super useful and borrowed it. Hey spicyj! :) Reviewed By: fkgozali Differential Revision: D5632592 fbshipit-source-id: dff43a5780b7404a3cc109c66c131cef4f4df429 --- React/Base/RCTUtils.m | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index 7844508b3..cfe9e23b7 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -264,14 +264,29 @@ void RCTUnsafeExecuteOnMainQueueSync(dispatch_block_t block) } } +static void RCTUnsafeExecuteOnMainQueueOnceSync(dispatch_once_t *onceToken, dispatch_block_t block) +{ + // The solution was borrowed from a post by Ben Alpert: + // https://benalpert.com/2014/04/02/dispatch-once-initialization-on-the-main-thread.html + // See also: https://www.mikeash.com/pyblog/friday-qa-2014-06-06-secrets-of-dispatch_once.html + if (RCTIsMainQueue()) { + dispatch_once(onceToken, block); + } else { + if (DISPATCH_EXPECT(*onceToken == 0L, NO)) { + dispatch_sync(dispatch_get_main_queue(), ^{ + dispatch_once(onceToken, block); + }); + } + } +} + CGFloat RCTScreenScale() { - static CGFloat scale; static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - RCTUnsafeExecuteOnMainQueueSync(^{ - scale = [UIScreen mainScreen].scale; - }); + static CGFloat scale; + + RCTUnsafeExecuteOnMainQueueOnceSync(&onceToken, ^{ + scale = [UIScreen mainScreen].scale; }); return scale;