aboutsummaryrefslogtreecommitdiff
path: root/DebugUtils/GTMSynchronizationAsserts.m
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.m
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.m')
-rw-r--r--DebugUtils/GTMSynchronizationAsserts.m93
1 files changed, 93 insertions, 0 deletions
diff --git a/DebugUtils/GTMSynchronizationAsserts.m b/DebugUtils/GTMSynchronizationAsserts.m
new file mode 100644
index 0000000..6c76550
--- /dev/null
+++ b/DebugUtils/GTMSynchronizationAsserts.m
@@ -0,0 +1,93 @@
+//
+// GTMSyncAsserts.m
+//
+// 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.
+//
+
+#import "GTMSynchronizationAsserts.h"
+
+#if DEBUG
+
+@implementation GTMSyncMonitorInternal
+
+- (instancetype)initWithSynchronizationObject:(id)object
+ allowRecursive:(BOOL)allowRecursive
+ functionName:(const char *)functionName {
+ self = [super init];
+ if (self) {
+ // In the thread's dictionary, we keep a counted set of the names
+ // of functions that are synchronizing on the object.
+ Class threadKey = [GTMSyncMonitorInternal class];
+ _objectKey = [NSValue valueWithNonretainedObject:object];
+ _functionName = functionName;
+
+ NSMutableDictionary *threadDict = [NSThread currentThread].threadDictionary;
+ NSMutableDictionary *counters = threadDict[threadKey];
+ if (counters == nil) {
+ counters = [NSMutableDictionary dictionary];
+ threadDict[(id)threadKey] = counters;
+ }
+ NSCountedSet *functionNamesCounter = counters[_objectKey];
+ NSUInteger numberOfSyncingFunctions = functionNamesCounter.count;
+
+ if (!allowRecursive) {
+ BOOL isTopLevelSyncScope = (numberOfSyncingFunctions == 0);
+ NSArray *stack = [NSThread callStackSymbols];
+ _GTMDevAssert(isTopLevelSyncScope,
+ @"*** Recursive sync on %@ at %s; previous sync at %@\n%@",
+ [object class], functionName, functionNamesCounter.allObjects,
+ [stack subarrayWithRange:NSMakeRange(1, stack.count - 1)]);
+ }
+
+ if (!functionNamesCounter) {
+ functionNamesCounter = [NSCountedSet set];
+ counters[_objectKey] = functionNamesCounter;
+ }
+ [functionNamesCounter addObject:@(functionName)];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ Class threadKey = [GTMSyncMonitorInternal class];
+
+ NSMutableDictionary *threadDict = [NSThread currentThread].threadDictionary;
+ NSMutableDictionary *counters = threadDict[threadKey];
+ NSCountedSet *functionNamesCounter = counters[_objectKey];
+ NSString *functionNameStr = @(_functionName);
+ NSUInteger numberOfSyncsByThisFunction = [functionNamesCounter countForObject:functionNameStr];
+ NSArray *stack = [NSThread callStackSymbols];
+ _GTMDevAssert(numberOfSyncsByThisFunction > 0, @"Sync not found on %@ at %s\n%@",
+ [_objectKey.nonretainedObjectValue class], _functionName,
+ [stack subarrayWithRange:NSMakeRange(1, stack.count - 1)]);
+ [functionNamesCounter removeObject:functionNameStr];
+ if (functionNamesCounter.count == 0) {
+ [counters removeObjectForKey:_objectKey];
+ }
+}
+
++ (NSArray *)functionsHoldingSynchronizationOnObject:(id)object {
+ Class threadKey = [GTMSyncMonitorInternal class];
+ NSValue *localObjectKey = [NSValue valueWithNonretainedObject:object];
+
+ NSMutableDictionary *threadDict = [NSThread currentThread].threadDictionary;
+ NSMutableDictionary *counters = threadDict[threadKey];
+ NSCountedSet *functionNamesCounter = counters[localObjectKey];
+ return functionNamesCounter.count > 0 ? functionNamesCounter.allObjects : nil;
+}
+
+@end
+
+#endif // DEBUG