// // 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