From 91fdd6d09d6390b67ff3258b6418437dca11d6e1 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Fri, 29 Apr 2016 13:47:13 -0400 Subject: Add GTMSynchronizationAsserts.h/m and a unit test file. These macros allow code to assert being in or not in a @sync-protected section, which is important when calling across methods or calling out to other classes or callbacks. --- DebugUtils/GTMSynchronizationAsserts.h | 124 +++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 DebugUtils/GTMSynchronizationAsserts.h (limited to 'DebugUtils/GTMSynchronizationAsserts.h') diff --git a/DebugUtils/GTMSynchronizationAsserts.h b/DebugUtils/GTMSynchronizationAsserts.h new file mode 100644 index 0000000..b026565 --- /dev/null +++ b/DebugUtils/GTMSynchronizationAsserts.h @@ -0,0 +1,124 @@ +// +// GTMSynchronizationAsserts.h +// +// Copyright 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#if !__has_feature(objc_arc) +#error "This file needs to be compiled with ARC enabled." +#endif + +#import + +#import "GTMDefines.h" // For _GTMDevAssert. + +// Macros to monitor synchronization blocks in debug builds. +// +// These report problems using _GTMDevAssert, which may be defined by the +// project or by GTMDefines.h +// +// GTMMonitorSynchronized Start monitoring a top-level-only @sync scope. +// Asserts if already inside a monitored @sync scope. +// GTMMonitorRecursiveSynchronized Start monitoring a top-level or recursive @sync +// scope. +// GTMCheckSynchronized Assert that the current execution is inside a monitored @sync +// scope. +// GTMCheckNotSynchronized Assert that the current execution is not inside a monitored +// @sync scope. +// +// Example usage: +// +// - (void)myExternalMethod { +// @synchronized(self) { +// GTMMonitorSynchronized(self) +// +// - (void)myInternalMethod { +// GTMCheckSynchronized(self); +// +// - (void)callMyCallbacks { +// GTMCheckNotSynchronized(self); +// +// GTMCheckNotSynchronized is available for verifying the code isn't +// in a deadlockable @sync state, important when posting notifications and +// invoking callbacks. +// +// Don't use GTMCheckNotSynchronized immediately before a @sync scope; the +// normal recursiveness check of GTMMonitorSynchronized can catch those. + +#if DEBUG + + #define __GTMMonitorSynchronizedVariableInner(varname, counter) \ + varname ## counter + #define __GTMMonitorSynchronizedVariable(varname, counter) \ + __GTMMonitorSynchronizedVariableInner(varname, counter) + + #define GTMMonitorSynchronized(obj) \ + NS_VALID_UNTIL_END_OF_SCOPE id \ + __GTMMonitorSynchronizedVariable(__monitor, __COUNTER__) = \ + [[GTMSyncMonitorInternal alloc] initWithSynchronizationObject:obj \ + allowRecursive:NO \ + functionName:__func__] + + #define GTMMonitorRecursiveSynchronized(obj) \ + NS_VALID_UNTIL_END_OF_SCOPE id \ + __GTMMonitorSynchronizedVariable(__monitor, __COUNTER__) = \ + [[GTMSyncMonitorInternal alloc] initWithSynchronizationObject:obj \ + allowRecursive:YES \ + functionName:__func__] + + #define GTMCheckSynchronized(obj) { \ + _GTMDevAssert( \ + [GTMSyncMonitorInternal functionsHoldingSynchronizationOnObject:obj], \ + @"GTMCheckSynchronized(" #obj ") failed: not sync'd" \ + @" on " #obj " in %s. Call stack:\n%@", \ + __func__, [NSThread callStackSymbols]); \ + } + + #define GTMCheckNotSynchronized(obj) { \ + _GTMDevAssert( \ + ![GTMSyncMonitorInternal functionsHoldingSynchronizationOnObject:obj], \ + @"GTMCheckNotSynchronized(" #obj ") failed: was sync'd" \ + @" on " #obj " in %s by %@. Call stack:\n%@", __func__, \ + [GTMSyncMonitorInternal functionsHoldingSynchronizationOnObject:obj], \ + [NSThread callStackSymbols]); \ + } + +// GTMSyncMonitorInternal is a private class that keeps track of the +// beginning and end of synchronized scopes, relying on ARC to release +// it at the end of a scope. +// +// This class should not be used directly, but only via the +// GTMMonitorSynchronized macro. +@interface GTMSyncMonitorInternal : NSObject { + NSValue *_objectKey; // The synchronize target object. + const char *_functionName; // The function containing the monitored sync block. +} + +- (instancetype)initWithSynchronizationObject:(id)object + allowRecursive:(BOOL)allowRecursive + functionName:(const char *)functionName; +// Return the names of the functions that hold sync on the object, or nil if none. ++ (NSArray *)functionsHoldingSynchronizationOnObject:(id)object; +@end + +#else + + // !DEBUG + #define GTMMonitorSynchronized(obj) do { } while (0) + #define GTMMonitorRecursiveSynchronized(obj) do { } while (0) + #define GTMCheckSynchronized(obj) do { } while (0) + #define GTMCheckNotSynchronized(obj) do { } while (0) + +#endif // DEBUG -- cgit v1.2.3