aboutsummaryrefslogtreecommitdiff
path: root/DebugUtils/GTMSynchronizationAsserts.h
diff options
context:
space:
mode:
authorGravatar Thomas Van Lenten <thomasvl@google.com>2016-04-29 13:47:13 -0400
committerGravatar Thomas Van Lenten <thomasvl@google.com>2016-04-29 13:47:13 -0400
commit91fdd6d09d6390b67ff3258b6418437dca11d6e1 (patch)
treeacf04a347085046a74a1ab14b8c1ee33b314b8fa /DebugUtils/GTMSynchronizationAsserts.h
parentc485d79b9289a4f6ff1402babbf44b6fba0aa6e7 (diff)
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.
Diffstat (limited to 'DebugUtils/GTMSynchronizationAsserts.h')
-rw-r--r--DebugUtils/GTMSynchronizationAsserts.h124
1 files changed, 124 insertions, 0 deletions
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 <Foundation/Foundation.h>
+
+#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