Skip to content

Commit c6da7d6

Browse files
GULLogger - issue count synchronous getters (#2610)
1 parent 378f4db commit c6da7d6

File tree

3 files changed

+95
-93
lines changed

3 files changed

+95
-93
lines changed

GoogleUtilities/Example/Tests/Logger/GULLoggerTest.m

Lines changed: 48 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@
3232

3333
extern BOOL getGULLoggerDebugMode(void);
3434

35-
extern NSUserDefaults *getGULLoggerUsetDefaults(void);
35+
extern CFStringRef getGULLoggerUsetDefaultsSuiteName(void);
36+
extern dispatch_queue_t getGULLoggerCounterQueue(void);
3637

3738
static NSString *const kMessageCode = @"I-COR000001";
3839

3940
@interface GULLoggerTest : XCTestCase
4041

4142
@property(nonatomic) NSString *randomLogString;
42-
43-
@property(nonatomic, strong) NSUserDefaults *defaults;
43+
@property(nonatomic) NSUserDefaults *loggerDefaults;
4444

4545
@end
4646

@@ -50,14 +50,18 @@ - (void)setUp {
5050
[super setUp];
5151
GULResetLogger();
5252

53-
// Stub NSUserDefaults for cleaner testing.
54-
_defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.google.logger_test"];
53+
self.loggerDefaults = [[NSUserDefaults alloc]
54+
initWithSuiteName:CFBridgingRelease(getGULLoggerUsetDefaultsSuiteName())];
5555
}
5656

5757
- (void)tearDown {
58-
[super tearDown];
58+
// Make sure all async operations have finished before starting a new test.
59+
[self drainQueue:getGULClientQueue()];
60+
[self drainQueue:getGULLoggerCounterQueue()];
5961

60-
_defaults = nil;
62+
self.loggerDefaults = nil;
63+
64+
[super tearDown];
6165
}
6266

6367
- (void)testMessageCodeFormat {
@@ -158,93 +162,58 @@ - (void)testGULLoggerLevelValues {
158162
- (void)testGetErrorWarningNumberBeforeLogDontCrash {
159163
GULResetLogger();
160164

161-
XCTestExpectation *getErrorCountExpectation =
162-
[self expectationWithDescription:@"getErrorCountExpectation"];
163-
XCTestExpectation *getWarningsCountExpectation =
164-
[self expectationWithDescription:@"getWarningsCountExpectation"];
165-
166-
GULNumberOfErrorsLogged(^(NSInteger count) {
167-
[getErrorCountExpectation fulfill];
168-
});
169-
170-
GULNumberOfWarningsLogged(^(NSInteger count) {
171-
[getWarningsCountExpectation fulfill];
172-
});
173-
174-
[self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ]
175-
timeout:0.5
176-
enforceOrder:YES];
165+
XCTAssertNoThrow(GULNumberOfErrorsLogged());
166+
XCTAssertNoThrow(GULNumberOfWarningsLogged());
177167
}
178168

179169
- (void)testErrorNumberIncrement {
180-
[getGULLoggerUsetDefaults() setInteger:10 forKey:kGULLoggerErrorCountKey];
170+
[self.loggerDefaults setInteger:10 forKey:kGULLoggerErrorCountKey];
181171

182172
GULLogError(@"my service", NO, kMessageCode, @"Message.");
183173

184-
XCTestExpectation *getErrorCountExpectation =
185-
[self expectationWithDescription:@"getErrorCountExpectation"];
186-
187-
GULNumberOfErrorsLogged(^(NSInteger count) {
188-
[getErrorCountExpectation fulfill];
189-
XCTAssertEqual(count, 11);
190-
});
191-
192-
[self waitForExpectationsWithTimeout:0.5 handler:NULL];
174+
[self drainQueue:getGULLoggerCounterQueue()];
175+
XCTAssertEqual(GULNumberOfErrorsLogged(), 11);
193176
}
194177

195178
- (void)testWarningNumberIncrement {
196-
[getGULLoggerUsetDefaults() setInteger:5 forKey:kGULLoggerWarningCountKey];
179+
[self.loggerDefaults setInteger:5 forKey:kGULLoggerWarningCountKey];
197180

198181
GULLogWarning(@"my service", NO, kMessageCode, @"Message.");
199182

200-
XCTestExpectation *getWarningsCountExpectation =
201-
[self expectationWithDescription:@"getWarningsCountExpectation"];
202-
203-
GULNumberOfWarningsLogged(^(NSInteger count) {
204-
[getWarningsCountExpectation fulfill];
205-
XCTAssertEqual(count, 6);
206-
});
207-
208-
[self waitForExpectationsWithTimeout:0.5 handler:NULL];
183+
[self drainQueue:getGULLoggerCounterQueue()];
184+
XCTAssertEqual(GULNumberOfWarningsLogged(), 6);
209185
}
210186

211187
- (void)testResetIssuesCount {
212-
[getGULLoggerUsetDefaults() setInteger:3 forKey:kGULLoggerErrorCountKey];
213-
[getGULLoggerUsetDefaults() setInteger:4 forKey:kGULLoggerWarningCountKey];
188+
[self.loggerDefaults setInteger:3 forKey:kGULLoggerErrorCountKey];
189+
[self.loggerDefaults setInteger:4 forKey:kGULLoggerWarningCountKey];
214190

215191
GULResetNumberOfIssuesLogged();
216192

217-
XCTestExpectation *getErrorCountExpectation =
218-
[self expectationWithDescription:@"getErrorCountExpectation"];
219-
XCTestExpectation *getWarningsCountExpectation =
220-
[self expectationWithDescription:@"getWarningsCountExpectation"];
221-
222-
GULNumberOfErrorsLogged(^(NSInteger count) {
223-
[getErrorCountExpectation fulfill];
224-
XCTAssertEqual(count, 0);
225-
});
226-
227-
GULNumberOfWarningsLogged(^(NSInteger count) {
228-
[getWarningsCountExpectation fulfill];
229-
XCTAssertEqual(count, 0);
230-
});
193+
XCTAssertEqual(GULNumberOfErrorsLogged(), 0);
194+
XCTAssertEqual(GULNumberOfWarningsLogged(), 0);
195+
}
231196

232-
[self waitForExpectations:@[ getErrorCountExpectation, getWarningsCountExpectation ]
233-
timeout:0.5
234-
enforceOrder:YES];
197+
- (void)testNumberOfIssuesLoggedNoDeadlock {
198+
[self dispatchSyncNestedDispatchCount:100
199+
queue:getGULLoggerCounterQueue()
200+
block:^{
201+
XCTAssertNoThrow(GULNumberOfErrorsLogged());
202+
XCTAssertNoThrow(GULNumberOfWarningsLogged());
203+
}];
235204
}
236205

237206
// Helper functions.
238207
- (BOOL)logExists {
239-
[self drainGULClientQueue];
208+
[self drainQueue:getGULClientQueue()];
240209
NSString *correctMsg =
241210
[NSString stringWithFormat:@"%@[%@] %@", @"my service", kMessageCode, self.randomLogString];
242211
return [self messageWasLogged:correctMsg];
243212
}
244213

245-
- (void)drainGULClientQueue {
214+
- (void)drainQueue:(dispatch_queue_t)queue {
246215
dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0);
247-
dispatch_async(getGULClientQueue(), ^{
216+
dispatch_barrier_async(queue, ^{
248217
dispatch_semaphore_signal(workerSemaphore);
249218
});
250219
dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER);
@@ -272,5 +241,19 @@ - (BOOL)messageWasLogged:(NSString *)message {
272241
#pragma clang pop
273242
}
274243

244+
- (void)dispatchSyncNestedDispatchCount:(NSInteger)count
245+
queue:(dispatch_queue_t)queue
246+
block:(dispatch_block_t)block {
247+
if (count < 0) {
248+
return;
249+
}
250+
251+
dispatch_sync(queue, ^{
252+
[self dispatchSyncNestedDispatchCount:count - 1 queue:queue block:block];
253+
block();
254+
NSLog(@"%@, depth: %ld", NSStringFromSelector(_cmd), (long)count);
255+
});
256+
}
257+
275258
@end
276259
#endif

GoogleUtilities/Logger/GULLogger.m

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -203,62 +203,79 @@ void GULLogBasic(GULLoggerLevel level,
203203

204204
#undef GUL_MAKE_LOGGER
205205

206-
#pragma mark - Number of errors and warnings
206+
#pragma mark - User defaults
207207

208-
NSUserDefaults *getGULLoggerUsetDefaults(void) {
209-
static NSUserDefaults *_userDefaults = nil;
210-
static dispatch_once_t onceToken;
211-
dispatch_once(&onceToken, ^{
212-
_userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"GoogleUtilities.Logger.GULLogger"];
213-
});
208+
// NSUserDefaults cannot be used due to a bug described in GULUserDefaults
209+
// GULUserDefaults cannot be used because GULLogger is a dependency for GULUserDefaults
210+
// We have to use C API deireclty here
214211

215-
return _userDefaults;
212+
CFStringRef getGULLoggerUsetDefaultsSuiteName(void) {
213+
return (__bridge CFStringRef) @"GoogleUtilities.Logger.GULLogger";
216214
}
217215

216+
NSInteger GULGetUserDefaultsIntegerForKey(NSString *key) {
217+
id value = (__bridge_transfer id)CFPreferencesCopyAppValue((__bridge CFStringRef)key,
218+
getGULLoggerUsetDefaultsSuiteName());
219+
if (![value isKindOfClass:[NSNumber class]]) {
220+
return 0;
221+
}
222+
223+
return [(NSNumber *)value integerValue];
224+
}
225+
226+
void GULLoggerUserDefaultsSetIntegerForKey(NSInteger count, NSString *key) {
227+
NSNumber *countNumber = @(count);
228+
CFPreferencesSetAppValue((__bridge CFStringRef)key, (__bridge CFNumberRef)countNumber,
229+
getGULLoggerUsetDefaultsSuiteName());
230+
CFPreferencesAppSynchronize(getGULLoggerUsetDefaultsSuiteName());
231+
}
232+
233+
#pragma mark - Number of errors and warnings
234+
218235
dispatch_queue_t getGULLoggerCounterQueue(void) {
219236
static dispatch_queue_t queue;
220237
static dispatch_once_t onceToken;
221238
dispatch_once(&onceToken, ^{
222-
queue = dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_SERIAL);
239+
queue =
240+
dispatch_queue_create("GoogleUtilities.GULLogger.counterQueue", DISPATCH_QUEUE_CONCURRENT);
223241
dispatch_set_target_queue(queue,
224242
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
225243
});
226244

227245
return queue;
228246
}
229247

230-
void GULAsyncGetUserDefaultsIntegerForKey(NSString *key, void (^completion)(NSInteger)) {
231-
dispatch_async(getGULLoggerCounterQueue(), ^{
232-
NSInteger count = [getGULLoggerUsetDefaults() integerForKey:key];
233-
dispatch_async(dispatch_get_main_queue(), ^{
234-
completion(count);
235-
});
248+
NSInteger GULSyncGetUserDefaultsIntegerForKey(NSString *key) {
249+
__block NSInteger integerValue = 0;
250+
dispatch_sync(getGULLoggerCounterQueue(), ^{
251+
integerValue = GULGetUserDefaultsIntegerForKey(key);
236252
});
253+
254+
return integerValue;
237255
}
238256

239-
void GULNumberOfErrorsLogged(void (^completion)(NSInteger)) {
240-
GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey, completion);
257+
NSInteger GULNumberOfErrorsLogged(void) {
258+
return GULSyncGetUserDefaultsIntegerForKey(kGULLoggerErrorCountKey);
241259
}
242260

243-
void GULNumberOfWarningsLogged(void (^completion)(NSInteger)) {
244-
GULAsyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey, completion);
261+
NSInteger GULNumberOfWarningsLogged(void) {
262+
return GULSyncGetUserDefaultsIntegerForKey(kGULLoggerWarningCountKey);
245263
}
246264

247265
void GULResetNumberOfIssuesLogged(void) {
248-
dispatch_async(getGULLoggerCounterQueue(), ^{
249-
[getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerErrorCountKey];
250-
[getGULLoggerUsetDefaults() setInteger:0 forKey:kGULLoggerWarningCountKey];
266+
dispatch_barrier_async(getGULLoggerCounterQueue(), ^{
267+
GULLoggerUserDefaultsSetIntegerForKey(0, kGULLoggerErrorCountKey);
268+
GULLoggerUserDefaultsSetIntegerForKey(0, kGULLoggerWarningCountKey);
251269
});
252270
}
253271

254272
void GULIncrementUserDefaultsIntegerForKey(NSString *key) {
255-
NSUserDefaults *defaults = getGULLoggerUsetDefaults();
256-
NSInteger errorCount = [defaults integerForKey:key];
257-
[defaults setInteger:errorCount + 1 forKey:key];
273+
NSInteger value = GULGetUserDefaultsIntegerForKey(key);
274+
GULLoggerUserDefaultsSetIntegerForKey(value + 1, key);
258275
}
259276

260277
void GULIncrementLogCountForLevel(GULLoggerLevel level) {
261-
dispatch_async(getGULLoggerCounterQueue(), ^{
278+
dispatch_barrier_async(getGULLoggerCounterQueue(), ^{
262279
if (level == GULLoggerLevelError) {
263280
GULIncrementUserDefaultsIntegerForKey(kGULLoggerErrorCountKey);
264281
} else if (level == GULLoggerLevelWarning) {

GoogleUtilities/Logger/Private/GULLogger.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,15 @@ extern void GULLogDebug(GULLoggerService service,
141141

142142
/**
143143
* Retrieve the number of errors that have been logged since the stat was last reset.
144+
* Calling this method can be comparably expensive, so it should not be called from main thread.
144145
*/
145-
extern void GULNumberOfErrorsLogged(void (^completion)(NSInteger));
146+
extern NSInteger GULNumberOfErrorsLogged(void);
146147

147148
/**
148149
* Retrieve the number of warnings that have been logged since the stat was last reset.
150+
* Calling this method can be comparably expensive, so it should not be called from main thread.
149151
*/
150-
extern void GULNumberOfWarningsLogged(void (^completion)(NSInteger));
152+
extern NSInteger GULNumberOfWarningsLogged(void);
151153

152154
/**
153155
* Reset number of errors and warnings that have been logged to 0.

0 commit comments

Comments
 (0)