diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index f7f5bb8b7e..2580d46177 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -692,29 +692,11 @@ rtc_static_library("rtc_base") { if (!build_with_mozilla) { public_deps += [ ":rtc_base_generic" ] } - if (is_win) { + if (is_win || is_mac || is_ios) { sources = [ "noop.cc", ] } - if (is_ios || is_mac) { - sources = [ - "noop.mm", - ] - public_deps += [ ":rtc_base_objc" ] - } -} - -if (is_ios || is_mac) { - rtc_source_set("rtc_base_objc") { - sources = [ - "thread_darwin.mm", - ] - deps = [ - ":rtc_base_generic", - ] - visibility = [ ":rtc_base" ] - } } rtc_static_library("rtc_base_generic") { @@ -894,6 +876,7 @@ rtc_static_library("rtc_base_generic") { if (is_ios || is_mac) { sources += [ "macifaddrs_converter.cc" ] + deps += [ "system:cocoa_threading" ] } if (rtc_use_x11) { diff --git a/rtc_base/system/BUILD.gn b/rtc_base/system/BUILD.gn index f22efe14a0..e5aa32b4ef 100644 --- a/rtc_base/system/BUILD.gn +++ b/rtc_base/system/BUILD.gn @@ -65,3 +65,15 @@ rtc_source_set("rtc_export") { "rtc_export.h", ] } + +if (is_mac || is_ios) { + rtc_source_set("cocoa_threading") { + sources = [ + "cocoa_threading.h", + "cocoa_threading.mm", + ] + deps = [ + "..:checks", + ] + } +} diff --git a/rtc_base/system/cocoa_threading.h b/rtc_base/system/cocoa_threading.h new file mode 100644 index 0000000000..518cb71786 --- /dev/null +++ b/rtc_base/system/cocoa_threading.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_COCOA_THREADING_H_ +#define RTC_BASE_SYSTEM_COCOA_THREADING_H_ + +// If Cocoa is to be used on more than one thread, it must know that the +// application is multithreaded. Since it's possible to enter Cocoa code +// from threads created by pthread_thread_create, Cocoa won't necessarily +// be aware that the application is multithreaded. Spawning an NSThread is +// enough to get Cocoa to set up for multithreaded operation, so this is done +// if necessary before pthread_thread_create spawns any threads. +// +// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html +void InitCocoaMultiThreading(); + +#endif // RTC_BASE_SYSTEM_COCOA_THREADING_H_ diff --git a/rtc_base/system/cocoa_threading.mm b/rtc_base/system/cocoa_threading.mm new file mode 100644 index 0000000000..c09862e7e5 --- /dev/null +++ b/rtc_base/system/cocoa_threading.mm @@ -0,0 +1,24 @@ +/* + * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/system/cocoa_threading.h" + +#import + +#include "rtc_base/checks.h" + +void InitCocoaMultiThreading() { + static BOOL is_cocoa_multithreaded = [NSThread isMultiThreaded]; + if (!is_cocoa_multithreaded) { + // +[NSObject class] is idempotent. + [NSThread detachNewThreadSelector:@selector(class) toTarget:[NSObject class] withObject:nil]; + is_cocoa_multithreaded = YES; + RTC_DCHECK([NSThread isMultiThreaded]); + } +} diff --git a/rtc_base/thread.cc b/rtc_base/thread.cc index 911ac16b07..8b0ef41dc6 100644 --- a/rtc_base/thread.cc +++ b/rtc_base/thread.cc @@ -33,6 +33,32 @@ #include "rtc_base/timeutils.h" #include "rtc_base/trace_event.h" +#if defined(WEBRTC_MAC) +#include "rtc_base/system/cocoa_threading.h" +/* + * These are forward-declarations for methods that are part of the + * ObjC runtime. They are declared in the private header objc-internal.h. + * These calls are what clang inserts when using @autoreleasepool in ObjC, + * but here they are used directly in order to keep this file C++. + * https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support + */ +extern "C" { +void* objc_autoreleasePoolPush(void); +void objc_autoreleasePoolPop(void* pool); +} + +namespace { +class ScopedAutoReleasePool { + public: + ScopedAutoReleasePool() : pool_(objc_autoreleasePoolPush()) {} + ~ScopedAutoReleasePool() { objc_autoreleasePoolPop(pool_); } + + private: + void* const pool_; +}; +} // namespace +#endif + namespace rtc { ThreadManager* ThreadManager::Instance() { @@ -62,11 +88,12 @@ Thread* Thread::Current() { } #if defined(WEBRTC_POSIX) -#if !defined(WEBRTC_MAC) ThreadManager::ThreadManager() : main_thread_ref_(CurrentThreadRef()) { +#if defined(WEBRTC_MAC) + InitCocoaMultiThreading(); +#endif pthread_key_create(&key_, nullptr); } -#endif Thread* ThreadManager::CurrentThread() { return static_cast(pthread_getspecific(key_)); @@ -301,7 +328,6 @@ void Thread::AssertBlockingIsAllowedOnCurrentThread() { } // static -#if !defined(WEBRTC_MAC) #if defined(WEBRTC_WIN) DWORD WINAPI Thread::PreRun(LPVOID pv) { #else @@ -310,6 +336,9 @@ void* Thread::PreRun(void* pv) { ThreadInit* init = static_cast(pv); ThreadManager::Instance()->SetCurrentThread(init->thread); rtc::SetCurrentThreadName(init->thread->name_.c_str()); +#if defined(WEBRTC_MAC) + ScopedAutoReleasePool pool; +#endif if (init->runnable) { init->runnable->Run(init->thread); } else { @@ -323,7 +352,6 @@ void* Thread::PreRun(void* pv) { return nullptr; #endif } -#endif void Thread::Run() { ProcessMessages(kForever); @@ -486,9 +514,6 @@ void Thread::Clear(MessageHandler* phandler, ClearInternal(phandler, id, removed); } -#if !defined(WEBRTC_MAC) -// Note that these methods have a separate implementation for mac and ios -// defined in webrtc/rtc_base/thread_darwin.mm. bool Thread::ProcessMessages(int cmsLoop) { // Using ProcessMessages with a custom clock for testing and a time greater // than 0 doesn't work, since it's not guaranteed to advance the custom @@ -499,6 +524,9 @@ bool Thread::ProcessMessages(int cmsLoop) { int cmsNext = cmsLoop; while (true) { +#if defined(WEBRTC_MAC) + ScopedAutoReleasePool pool; +#endif Message msg; if (!Get(&msg, cmsNext)) return !IsQuitting(); @@ -511,7 +539,6 @@ bool Thread::ProcessMessages(int cmsLoop) { } } } -#endif bool Thread::WrapCurrentWithThreadManager(ThreadManager* thread_manager, bool need_synchronize_access) { diff --git a/rtc_base/thread_darwin.mm b/rtc_base/thread_darwin.mm deleted file mode 100644 index e64d6ebe0b..0000000000 --- a/rtc_base/thread_darwin.mm +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtc_base/thread.h" - -#import - -#include "rtc_base/platform_thread.h" -#include "rtc_base/timeutils.h" // for TimeAfter, TimeUntil - -/* - * This file contains platform-specific implementations for several - * methods in rtc::Thread. - */ - -namespace { -void InitCocoaMultiThreading() { - if ([NSThread isMultiThreaded] == NO) { - // The sole purpose of this autorelease pool is to avoid a console - // message on Leopard that tells us we're autoreleasing the thread - // with no autorelease pool in place. - @autoreleasepool { - [NSThread detachNewThreadSelector:@selector(class) - toTarget:[NSObject class] - withObject:nil]; - } - } - - RTC_DCHECK([NSThread isMultiThreaded]); -} -} - -namespace rtc { - -ThreadManager::ThreadManager() : main_thread_ref_(CurrentThreadRef()) { - pthread_key_create(&key_, nullptr); - // This is necessary to alert the cocoa runtime of the fact that - // we are running in a multithreaded environment. - InitCocoaMultiThreading(); -} - -// static -void* Thread::PreRun(void* pv) { - ThreadInit* init = static_cast(pv); - ThreadManager::Instance()->SetCurrentThread(init->thread); - rtc::SetCurrentThreadName(init->thread->name_.c_str()); - @autoreleasepool { - if (init->runnable) { - init->runnable->Run(init->thread); - } else { - init->thread->Run(); - } - } - ThreadManager::Instance()->SetCurrentThread(nullptr); - delete init; - return nullptr; -} - -bool Thread::ProcessMessages(int cmsLoop) { - int64_t msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop); - int cmsNext = cmsLoop; - - while (true) { - @autoreleasepool { - Message msg; - if (!Get(&msg, cmsNext)) - return !IsQuitting(); - Dispatch(&msg); - - if (cmsLoop != kForever) { - cmsNext = static_cast(TimeUntil(msEnd)); - if (cmsNext < 0) - return true; - } - } - } -} -} // namespace rtc