diff --git a/RealtimeWatchdog.podspec b/RealtimeWatchdog.podspec index 523be64..6ff5af4 100644 --- a/RealtimeWatchdog.podspec +++ b/RealtimeWatchdog.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RealtimeWatchdog" - s.version = "1.0.2" + s.version = "1.0.3" s.summary = "A library for iOS audio which acts as a watchdog for unsafe activity on the audio thread." s.description = <<-DESC This library for iOS acts as a watchdog for activities on the Core Audio thread that are considered unsafe: @@ -17,7 +17,7 @@ It won’t catch everything, and it won’t catch anything in Apple’s own syst s.homepage = "https://github.com/TheAmazingAudioEngine/RealtimeWatchdog" s.license = 'zlib' s.author = { "Michael Tyson" => "michael@atastypixel.com" } - s.source = { :git => "https://github.com/TheAmazingAudioEngine/RealtimeWatchdog.git", :tag => "1.0.2" } + s.source = { :git => "https://github.com/TheAmazingAudioEngine/RealtimeWatchdog.git", :tag => "1.0.3" } s.ios.deployment_target = '9.0' s.source_files = 'RealtimeWatchdog/*' s.pod_target_xcconfig = { 'OTHER_CFLAGS' => '-fno-modules' } diff --git a/RealtimeWatchdog.xcodeproj/project.pbxproj b/RealtimeWatchdog.xcodeproj/project.pbxproj index 7aae1b0..f5a8f05 100644 --- a/RealtimeWatchdog.xcodeproj/project.pbxproj +++ b/RealtimeWatchdog.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 4C636E421D0D87AF005A380B /* AERealtimeWatchdog-simulator-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C636E3E1D0D87AF005A380B /* AERealtimeWatchdog-simulator-x86_64.s */; settings = {COMPILER_FLAGS = "-fno-modules"; }; }; 4C636E431D0D87AF005A380B /* AERealtimeWatchdog.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C636E401D0D87AF005A380B /* AERealtimeWatchdog.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 4C636E461D0D882E005A380B /* AERealtimeWatchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C636E3F1D0D87AF005A380B /* AERealtimeWatchdog.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A40738DF271A3EC100C636FB /* AERealtimeWatchdogExt.m in Sources */ = {isa = PBXBuildFile; fileRef = A40738DE271A3EC100C636FB /* AERealtimeWatchdogExt.m */; }; + A43D51A827212EDF00C931B1 /* AERealtimeWatchdogExt.h in Headers */ = {isa = PBXBuildFile; fileRef = A40738DD271A339600C636FB /* AERealtimeWatchdogExt.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -18,7 +20,9 @@ 4C636E3D1D0D87AF005A380B /* AERealtimeWatchdog-arm64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "AERealtimeWatchdog-arm64.s"; sourceTree = ""; }; 4C636E3E1D0D87AF005A380B /* AERealtimeWatchdog-simulator-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "AERealtimeWatchdog-simulator-x86_64.s"; sourceTree = ""; }; 4C636E3F1D0D87AF005A380B /* AERealtimeWatchdog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AERealtimeWatchdog.h; sourceTree = ""; }; - 4C636E401D0D87AF005A380B /* AERealtimeWatchdog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AERealtimeWatchdog.m; sourceTree = ""; }; + 4C636E401D0D87AF005A380B /* AERealtimeWatchdog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AERealtimeWatchdog.m; sourceTree = ""; usesTabs = 0; }; + A40738DD271A339600C636FB /* AERealtimeWatchdogExt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AERealtimeWatchdogExt.h; sourceTree = ""; }; + A40738DE271A3EC100C636FB /* AERealtimeWatchdogExt.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AERealtimeWatchdogExt.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -55,6 +59,8 @@ 4C636E3E1D0D87AF005A380B /* AERealtimeWatchdog-simulator-x86_64.s */, 4C636E3F1D0D87AF005A380B /* AERealtimeWatchdog.h */, 4C636E401D0D87AF005A380B /* AERealtimeWatchdog.m */, + A40738DD271A339600C636FB /* AERealtimeWatchdogExt.h */, + A40738DE271A3EC100C636FB /* AERealtimeWatchdogExt.m */, ); path = RealtimeWatchdog; sourceTree = ""; @@ -67,6 +73,7 @@ buildActionMask = 2147483647; files = ( 4C636E461D0D882E005A380B /* AERealtimeWatchdog.h in Headers */, + A43D51A827212EDF00C931B1 /* AERealtimeWatchdogExt.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -96,7 +103,7 @@ 4C636E291D0D878B005A380B /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1600; ORGANIZATIONNAME = "A Tasty Pixel"; TargetAttributes = { 4C636E301D0D878B005A380B = { @@ -127,6 +134,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A40738DF271A3EC100C636FB /* AERealtimeWatchdogExt.m in Sources */, 4C636E421D0D87AF005A380B /* AERealtimeWatchdog-simulator-x86_64.s in Sources */, 4C636E431D0D87AF005A380B /* AERealtimeWatchdog.m in Sources */, 4C636E411D0D87AF005A380B /* AERealtimeWatchdog-arm64.s in Sources */, @@ -185,7 +193,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PUBLIC_HEADERS_FOLDER_PATH = "$(PROJECT_NAME)"; @@ -236,7 +244,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; PUBLIC_HEADERS_FOLDER_PATH = "$(PROJECT_NAME)"; SDKROOT = iphoneos; diff --git a/RealtimeWatchdog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/RealtimeWatchdog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/RealtimeWatchdog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/RealtimeWatchdog/AERealtimeWatchdog-simulator-x86_64.s b/RealtimeWatchdog/AERealtimeWatchdog-simulator-x86_64.s index f1d6036..40286e4 100644 --- a/RealtimeWatchdog/AERealtimeWatchdog-simulator-x86_64.s +++ b/RealtimeWatchdog/AERealtimeWatchdog-simulator-x86_64.s @@ -43,7 +43,6 @@ * limitations under the License. */ - #include #include "AERealtimeWatchdog.h" #if __x86_64__ && TARGET_OS_SIMULATOR && REALTIME_WATCHDOG_ENABLED diff --git a/RealtimeWatchdog/AERealtimeWatchdog.h b/RealtimeWatchdog/AERealtimeWatchdog.h index da9d6e9..2aa6bc5 100644 --- a/RealtimeWatchdog/AERealtimeWatchdog.h +++ b/RealtimeWatchdog/AERealtimeWatchdog.h @@ -31,3 +31,20 @@ #define REALTIME_WATCHDOG_ENABLED 1 #endif + +#ifndef __ASSEMBLER__ + +#import "AERealtimeWatchdogExt.h" + +/*! + * Pause monitoring until AERealtimeWatchdogResume called + */ +void AERealtimeWatchdogPause(void); + +/*! + * Resume monitoring + */ +void AERealtimeWatchdogResume(void); + +#endif + diff --git a/RealtimeWatchdog/AERealtimeWatchdog.m b/RealtimeWatchdog/AERealtimeWatchdog.m index af4a215..b6a7e6a 100755 --- a/RealtimeWatchdog/AERealtimeWatchdog.m +++ b/RealtimeWatchdog/AERealtimeWatchdog.m @@ -26,11 +26,23 @@ // #import "AERealtimeWatchdog.h" +#import +#import + +static BOOL __paused = NO; + +void AERealtimeWatchdogPause(void) { + __paused = YES; +} + +void AERealtimeWatchdogResume(void) { + __paused = NO; +} + #ifdef REALTIME_WATCHDOG_ENABLED #import #import -#import #import #import #include @@ -40,8 +52,6 @@ //#define REPORT_EVERY_INFRACTION - - static void AERealtimeWatchdogUnsafeActivityWarning(const char * activity) { #ifndef REPORT_EVERY_INFRACTION static BOOL once = NO; @@ -57,6 +67,25 @@ static void AERealtimeWatchdogUnsafeActivityWarning(const char * activity) { #endif } +#if TARGET_OS_MACCATALYST +char kAERealtimeWatchdogThreadName[64] = "com.apple.audio.IOThread.client"; +#else +char kAERealtimeWatchdogThreadName[64] = "AURemoteIO::IOThread"; +#endif + +// This function will be called before main() to query the process info for the current platform +void InitRealtimeThreadName(void) __attribute__((constructor)); +void InitRealtimeThreadName(void) { + if (@available(iOS 14.0, *)) { + if (NSProcessInfo.processInfo.isiOSAppOnMac) { + strncpy(kAERealtimeWatchdogThreadName, "com.apple.audio.IOThread.client", sizeof(kAERealtimeWatchdogThreadName)); + } + } +} + + + +pthread_t sAERealtimeThread = 0; BOOL AERealtimeWatchdogIsOnRealtimeThread(void) { pthread_t thread = pthread_self(); @@ -67,9 +96,13 @@ BOOL AERealtimeWatchdogIsOnRealtimeThread(void) { if ( pthread_getschedparam(thread, &policy, ¶m) == 0 && param.sched_priority >= sched_get_priority_max(policy) ) { return YES; } +#elif defined(USE_WATCHDOG_REGISTRATION_FUNCTIONS) // Trying a thread registration test + if (sAERealtimeThread == thread) { + return YES; + } #else - char name[21]; - if ( pthread_getname_np(thread, name, sizeof(name)) == 0 && !strcmp(name, "AURemoteIO::IOThread") ) { + char name[64]; + if ( pthread_getname_np(thread, name, sizeof(name)) == 0 && !strcmp(name, kAERealtimeWatchdogThreadName) ) { return YES; } #endif @@ -78,9 +111,6 @@ BOOL AERealtimeWatchdogIsOnRealtimeThread(void) { } - - - #pragma mark - Overrides // Signatures for the functions we'll override @@ -94,7 +124,9 @@ BOOL AERealtimeWatchdogIsOnRealtimeThread(void) { typedef int (*objc_sync_enter_t)(id obj); typedef id (*objc_storeStrong_t)(id *object, id value); typedef id (*objc_loadWeak_t)(id *object); +typedef id (*objc_loadWeakRetained_t)(id *object); typedef id (*objc_storeWeak_t)(id *object, id value); +typedef id (*objc_retainAutoreleasedReturnValue_t)(id *object, id value); typedef id (*object_getIvar_t)(id object, Ivar ivar); typedef id (*objc_msgSend_t)(void); typedef ssize_t (*send_t)(int socket, const void *buffer, size_t length, int flags); @@ -134,7 +166,6 @@ typedef ssize_t (*recvfrom_t)(int socket, void *restrict buffer, size_t length, typedef dispatch_semaphore_t (*dispatch_semaphore_create_t)(long value); typedef long (*dispatch_semaphore_wait_t)(dispatch_semaphore_t dsema, dispatch_time_t timeout); -typedef long (*dispatch_semaphore_signal_t)(dispatch_semaphore_t dsema); // Overrides @@ -144,7 +175,7 @@ typedef ssize_t (*recvfrom_t)(int socket, void *restrict buffer, size_t length, dispatch_once(&onceToken, ^{ \ funcptr = (name##_t) dlsym(RTLD_NEXT, #name); \ }); \ - if ( AERealtimeWatchdogIsOnRealtimeThread() ) AERealtimeWatchdogUnsafeActivityWarning(msg); + if ( !__paused && AERealtimeWatchdogIsOnRealtimeThread() ) AERealtimeWatchdogUnsafeActivityWarning(msg); #define CHECK_FUNCTION(name) CHECK_FUNCTION_MSG(name, #name) @@ -200,12 +231,24 @@ id objc_loadWeak(id * object) { return funcptr(object); } +id objc_loadWeakRetained(id * object); +id objc_loadWeakRetained(id * object) { + CHECK_FUNCTION_MSG(objc_loadWeakRetained, "weak load"); + return funcptr(object); +} + id objc_storeWeak(id * object, id value); id objc_storeWeak(id * object, id value) { CHECK_FUNCTION_MSG(objc_storeWeak, "weak store"); return funcptr(object,value); } +id objc_retainAutoreleasedReturnValue(id * object, id value); +id objc_retainAutoreleasedReturnValue(id * object, id value) { + CHECK_FUNCTION_MSG(objc_retainAutoreleasedReturnValue, "retain autoreleased return value"); + return funcptr(object,value); +} + id object_getIvar(id object, Ivar value); id object_getIvar(id object, Ivar value) { CHECK_FUNCTION_MSG(object_getIvar, "ivar fetch"); @@ -383,9 +426,4 @@ long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout return funcptr(dsema, timeout); } -long dispatch_semaphore_signal(dispatch_semaphore_t dsema) { - CHECK_FUNCTION(dispatch_semaphore_signal); - return funcptr(dsema); -} - #endif diff --git a/RealtimeWatchdog/AERealtimeWatchdogExt.h b/RealtimeWatchdog/AERealtimeWatchdogExt.h new file mode 100644 index 0000000..d6d913c --- /dev/null +++ b/RealtimeWatchdog/AERealtimeWatchdogExt.h @@ -0,0 +1,55 @@ +// +// AERealtimeWatchdogMonitor.h +// TheAmazingAudioEngine +// +// Created by Michael Tyson on 12/06/2016. +// Idea by Taylor Holliday +// Copyright © 2016 A Tasty Pixel. All rights reserved. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +// Uncomment the following to use the thread registration functions. You must call +// AERealtimeWatchdogStartMonitoring and AERealtimeWatchdogStopMonitoring from the realtime thread. +//#define USE_WATCHDOG_REGISTRATION_FUNCTIONS 1 + + +#ifndef __ASSEMBLER__ +#if defined(REALTIME_WATCHDOG_ENABLED) && defined(USE_WATCHDOG_REGISTRATION_FUNCTIONS) + +#if defined(__cplusplus) +extern "C" { +#endif + // Call this at the very top of the playback and record callbacks + void AERealtimeWatchdogStartMonitoring(void); + // Call this at the very end of the playback and record callbacks + void AERealtimeWatchdogStopMonitoring(void); +#if defined(__cplusplus) +} +#endif + +#else // !REALTIME_WATCHDOG_ENABLED + + // Stubbed out for release + static inline void AERealtimeWatchdogStartMonitoring(void) {}; + static inline void AERealtimeWatchdogStopMonitoring(void) {}; + +#endif // REALTIME_WATCHDOG_ENABLED +#endif // REALTIME_WATCHDOG_INCLUDED_BY_ASM + diff --git a/RealtimeWatchdog/AERealtimeWatchdogExt.m b/RealtimeWatchdog/AERealtimeWatchdogExt.m new file mode 100644 index 0000000..0fabb12 --- /dev/null +++ b/RealtimeWatchdog/AERealtimeWatchdogExt.m @@ -0,0 +1,65 @@ +// +// AERealtimeWatchdogMonitor.h +// TheAmazingAudioEngine +// +// Created by Michael Tyson on 12/06/2016. +// Idea by Taylor Holliday +// Copyright © 2016 A Tasty Pixel. All rights reserved. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#import +#import +#import + +#import "AERealtimeWatchdog.h" + +#if defined(REALTIME_WATCHDOG_ENABLED) && defined(USE_WATCHDOG_REGISTRATION_FUNCTIONS) + + +extern pthread_t sAERealtimeThread; +extern char kAERealtimeWatchdogThreadName[64]; + +// Call this at the very top of the playback and record callbacks +void AERealtimeWatchdogStartMonitoring(void) { + pthread_t thread = pthread_self(); + + // All we really need is to assign sAERealtimeThread, but do a few sanity checks + char name[64]; + if ( pthread_getname_np(thread, name, sizeof(name)) == 0 && !strcmp(name, kAERealtimeWatchdogThreadName) ) { + if (sAERealtimeThread != NULL) { + printf("AERealtimeWatchdog: Unmatched start/stop!\n"); + } + sAERealtimeThread = thread; + } else { + printf("AERealtimeWatchdog: %s not called from the realtime thread!\n", __FUNCTION__); + sAERealtimeThread = NULL; + } +} + +// Call this at the very end of the playback and record callbacks +void AERealtimeWatchdogStopMonitoring(void) { + if (sAERealtimeThread == NULL) { + printf("AERealtimeWatchdog: Unmatched start/stop!\n"); + } + sAERealtimeThread = NULL; +} + +#endif