aboutsummaryrefslogtreecommitdiff
path: root/DebugUtils/GTMSynchronizationAsserts.m
blob: 6c76550e514c596598fc6dfa0787fdbaae9c7c61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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