Skip to content

Commit c57118c

Browse files
authored
[LP-10948] Potential fix for pause crash (#328)
* Potential fix for pause crash * Fixing tests
1 parent c501fd3 commit c57118c

File tree

3 files changed

+149
-150
lines changed

3 files changed

+149
-150
lines changed

Leanplum-SDK/Classes/Features/Events/LPEventCallbackManager.m

Lines changed: 79 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -44,92 +44,105 @@ + (void)addEventCallbackAt:(NSInteger)index
4444
onSuccess:(LPNetworkResponseBlock)responseBlock
4545
onError:(LPNetworkErrorBlock)errorBlock
4646
{
47-
if (!responseBlock && !errorBlock) {
48-
return;
49-
}
50-
51-
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
52-
LPEventCallback *callback = [[LPEventCallback alloc] initWithResponseBlock:responseBlock
53-
errorBlock:errorBlock];
54-
NSNumber *atIndex = @(index);
55-
56-
if (callbackMap && callback && atIndex) {
57-
callbackMap[atIndex] = callback;
47+
@synchronized ([LPEventCallbackManager eventCallbackMap])
48+
{
49+
if (!responseBlock && !errorBlock) {
50+
return;
51+
}
52+
53+
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
54+
LPEventCallback *callback = [[LPEventCallback alloc] initWithResponseBlock:responseBlock
55+
errorBlock:errorBlock];
56+
57+
NSNumber *atIndex = @(index);
58+
59+
if (callbackMap && callback && atIndex) {
60+
callbackMap[atIndex] = callback;
61+
}
62+
[[LPCountAggregator sharedAggregator] incrementCount:@"add_event_callback_at"];
5863
}
59-
[[LPCountAggregator sharedAggregator] incrementCount:@"add_event_callback_at"];
6064
}
6165

6266
+ (void)invokeSuccessCallbacksOnResponses:(id)responses
6367
requests:(NSArray *)requests
6468
operation:(id<LPNetworkOperationProtocol>)operation
6569
{
66-
// Invoke and remove the callbacks that have errors.
67-
[LPEventCallbackManager invokeErrorCallbacksOnResponses:responses];
68-
69-
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
70-
NSMutableDictionary *updatedCallbackMap = [NSMutableDictionary new];
71-
NSMutableDictionary *activeResponseMap = [NSMutableDictionary new];
72-
73-
for (NSNumber *indexObject in callbackMap.allKeys) {
74-
NSInteger index = [indexObject integerValue];
75-
LPEventCallback *eventCallback = callbackMap[indexObject];
76-
77-
// If index is in range, execute and remove it.
78-
// If not, requests are in the future. Update the index.
79-
[callbackMap removeObjectForKey:indexObject];
80-
if (index >= requests.count) {
81-
index -= requests.count;
82-
updatedCallbackMap[@(index)] = eventCallback;
83-
} else if (eventCallback.responseBlock) {
84-
activeResponseMap[indexObject] = [eventCallback.responseBlock copy];
70+
@synchronized ([LPEventCallbackManager eventCallbackMap])
71+
{
72+
// Invoke and remove the callbacks that have errors.
73+
[LPEventCallbackManager invokeErrorCallbacksOnResponses:responses];
74+
75+
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
76+
NSMutableDictionary *updatedCallbackMap = [NSMutableDictionary new];
77+
NSMutableDictionary *activeResponseMap = [NSMutableDictionary new];
78+
79+
for (NSNumber *indexObject in callbackMap.allKeys) {
80+
NSInteger index = [indexObject integerValue];
81+
LPEventCallback *eventCallback = callbackMap[indexObject];
82+
83+
// If index is in range, execute and remove it.
84+
// If not, requests are in the future. Update the index.
85+
[callbackMap removeObjectForKey:indexObject];
86+
if (index >= requests.count) {
87+
index -= requests.count;
88+
updatedCallbackMap[@(index)] = eventCallback;
89+
} else if (eventCallback.responseBlock) {
90+
activeResponseMap[indexObject] = [eventCallback.responseBlock copy];
91+
}
8592
}
93+
[callbackMap addEntriesFromDictionary:updatedCallbackMap];
94+
95+
// Execute responses afterwards to prevent index collision.
96+
[activeResponseMap enumerateKeysAndObjectsUsingBlock:^(NSNumber *indexObject, LPNetworkResponseBlock responseBlock, BOOL *stop) {
97+
NSInteger index = [indexObject integerValue];
98+
id response = [LPResponse getResponseAt:index fromDictionary:responses];
99+
responseBlock(operation, response);
100+
}];
101+
[[LPCountAggregator sharedAggregator] incrementCount:@"invoke_success_callbacks_on_responses"];
86102
}
87-
[callbackMap addEntriesFromDictionary:updatedCallbackMap];
88-
89-
// Execute responses afterwards to prevent index collision.
90-
[activeResponseMap enumerateKeysAndObjectsUsingBlock:^(NSNumber *indexObject, LPNetworkResponseBlock responseBlock, BOOL *stop) {
91-
NSInteger index = [indexObject integerValue];
92-
id response = [LPResponse getResponseAt:index fromDictionary:responses];
93-
responseBlock(operation, response);
94-
}];
95-
[[LPCountAggregator sharedAggregator] incrementCount:@"invoke_success_callbacks_on_responses"];
96103
}
97104

98105
+ (void)invokeErrorCallbacksOnResponses:(id)responses
99106
{
100-
// Handle errors that don't return an HTTP error code.
101-
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
102-
for (NSUInteger i = 0; i < [LPResponse numResponsesInDictionary:responses]; i++) {
103-
NSDictionary *response = [LPResponse getResponseAt:i fromDictionary:responses];
104-
if ([LPResponse isResponseSuccess:response]) {
105-
continue;
106-
}
107-
108-
NSString *errorMessage = @"API error";
109-
NSString *responseError = [LPResponse getResponseError:response];
110-
if (responseError) {
111-
errorMessage = [NSString stringWithFormat:@"API error: %@", errorMessage];
112-
}
113-
NSLog(@"Leanplum: %@", errorMessage);
114-
115-
LPEventCallback *callback = callbackMap[@(i)];
116-
if (callback) {
117-
[callbackMap removeObjectForKey:@(i)];
118-
NSError *error = [NSError errorWithDomain:@"Leanplum" code:2
119-
userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
120-
[callback invokeError:error];
107+
@synchronized ([LPEventCallbackManager eventCallbackMap])
108+
{
109+
// Handle errors that don't return an HTTP error code.
110+
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
111+
for (NSUInteger i = 0; i < [LPResponse numResponsesInDictionary:responses]; i++) {
112+
NSDictionary *response = [LPResponse getResponseAt:i fromDictionary:responses];
113+
if ([LPResponse isResponseSuccess:response]) {
114+
continue;
115+
}
116+
117+
NSString *errorMessage = @"API error";
118+
NSString *responseError = [LPResponse getResponseError:response];
119+
if (responseError) {
120+
errorMessage = [NSString stringWithFormat:@"API error: %@", errorMessage];
121+
}
122+
NSLog(@"Leanplum: %@", errorMessage);
123+
124+
LPEventCallback *callback = callbackMap[@(i)];
125+
if (callback) {
126+
[callbackMap removeObjectForKey:@(i)];
127+
NSError *error = [NSError errorWithDomain:@"Leanplum" code:2
128+
userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
129+
[callback invokeError:error];
130+
}
121131
}
132+
[[LPCountAggregator sharedAggregator] incrementCount:@"invoke_error_callbacks_on_responses"];
122133
}
123-
[[LPCountAggregator sharedAggregator] incrementCount:@"invoke_error_callbacks_on_responses"];
124134
}
125135

126136
+ (void)invokeErrorCallbacksWithError:(NSError *)error
127137
{
128-
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
129-
for (LPEventCallback *callback in callbackMap.allValues) {
130-
[callback invokeError:error];
138+
@synchronized ([LPEventCallbackManager eventCallbackMap])
139+
{
140+
NSMutableDictionary *callbackMap = [LPEventCallbackManager eventCallbackMap];
141+
for (LPEventCallback *callback in callbackMap.allValues) {
142+
[callback invokeError:error];
143+
}
144+
[callbackMap removeAllObjects];
131145
}
132-
[callbackMap removeAllObjects];
133146
}
134147

135148
@end

Leanplum-SDK/Classes/Managers/Networking/LPRequestSender.m

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -137,22 +137,22 @@ - (void)sendEventually:(id<LPRequesting>)request sync:(BOOL)sync
137137
request.sent = YES;
138138

139139
void (^operationBlock)(void) = ^void() {
140+
LP_TRY
140141
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
141142
NSString *uuid = [userDefaults objectForKey:LEANPLUM_DEFAULTS_UUID_KEY];
142143
NSInteger count = [LPEventDataManager count];
143144
if (!uuid || count % MAX_EVENTS_PER_API_CALL == 0) {
144145
uuid = [self generateUUID];
145146
}
146147

147-
@synchronized ([LPEventCallbackManager eventCallbackMap]) {
148-
NSMutableDictionary *args = [self createArgsDictionaryForRequest:request];
149-
args[LP_PARAM_UUID] = uuid;
150-
[LPEventDataManager addEvent:args];
148+
NSMutableDictionary *args = [self createArgsDictionaryForRequest:request];
149+
args[LP_PARAM_UUID] = uuid;
150+
[LPEventDataManager addEvent:args];
151151

152-
[LPEventCallbackManager addEventCallbackAt:count
153-
onSuccess:request.responseBlock
154-
onError:request.errorBlock];
155-
}
152+
[LPEventCallbackManager addEventCallbackAt:count
153+
onSuccess:request.responseBlock
154+
onError:request.errorBlock];
155+
LP_END_TRY
156156
};
157157

158158
if (sync) {
@@ -386,24 +386,18 @@ - (void)sendRequests:(BOOL)sync
386386
[self.uiTimeoutTimer invalidate];
387387
self.uiTimeoutTimer = nil;
388388

389-
// We need to lock sendNowCallbackMap so that new event callback won't be triggered
390-
// right after it gets deleted.
391-
@synchronized ([LPEventCallbackManager eventCallbackMap]) {
392-
LP_TRY
393-
// Delete events on success.
394-
[LPEventDataManager deleteEventsWithLimit:requestsToSend.count];
389+
// Delete events on success.
390+
[LPEventDataManager deleteEventsWithLimit:requestsToSend.count];
395391

396-
// Send another request if the last request had maximum events per api call.
397-
if (requestsToSend.count == MAX_EVENTS_PER_API_CALL) {
398-
[self sendRequests:sync];
399-
}
400-
LP_END_TRY
392+
// Send another request if the last request had maximum events per api call.
393+
if (requestsToSend.count == MAX_EVENTS_PER_API_CALL) {
394+
[self sendRequests:sync];
395+
}
401396

402-
if (!self.didUiTimeout) {
403-
[LPEventCallbackManager invokeSuccessCallbacksOnResponses:json
404-
requests:requestsToSend
405-
operation:operation];
406-
}
397+
if (!self.didUiTimeout) {
398+
[LPEventCallbackManager invokeSuccessCallbacksOnResponses:json
399+
requests:requestsToSend
400+
operation:operation];
407401
}
408402
dispatch_semaphore_signal(semaphore);
409403
LP_END_TRY

Leanplum-SDK/Classes/Managers/Networking/LeanplumRequest.m

Lines changed: 52 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -295,71 +295,63 @@ - (void)sendRequests:(BOOL)async
295295
dispatch_semaphore_signal(semaphore);
296296
return;
297297
}
298-
299-
// We need to lock sendNowCallbackMap so that new event callback won't be triggered
300-
// right after it gets deleted.
301-
@synchronized ([LPEventCallbackManager eventCallbackMap]) {
302-
LP_TRY
303-
// Delete events on success.
304-
[LPEventDataManager deleteEventsWithLimit:requestsToSend.count];
305-
306-
// Send another request if the last request had maximum events per api call.
307-
if (requestsToSend.count == MAX_EVENTS_PER_API_CALL) {
308-
[self sendRequests:async];
309-
}
310-
LP_END_TRY
311298

312-
[LPEventCallbackManager invokeSuccessCallbacksOnResponses:json
313-
requests:requestsToSend
314-
operation:operation];
299+
// Delete events on success.
300+
[LPEventDataManager deleteEventsWithLimit:requestsToSend.count];
301+
302+
// Send another request if the last request had maximum events per api call.
303+
if (requestsToSend.count == MAX_EVENTS_PER_API_CALL) {
304+
[self sendRequests:async];
315305
}
306+
307+
[LPEventCallbackManager invokeSuccessCallbacksOnResponses:json
308+
requests:requestsToSend
309+
operation:operation];
316310
dispatch_semaphore_signal(semaphore);
317311
LP_END_TRY
318-
319312
} errorHandler:^(id<LPNetworkOperationProtocol> completedOperation, NSError *err) {
320313
LP_TRY
321314
if ([weakOperation isCancelled]) {
322315
dispatch_semaphore_signal(semaphore);
323316
return;
324317
}
325318

326-
@synchronized ([LPEventCallbackManager eventCallbackMap]) {
327-
// Retry on 500 and other network failures.
328-
NSInteger httpStatusCode = completedOperation.HTTPStatusCode;
329-
if (httpStatusCode == 408
330-
|| (httpStatusCode >= 500 && httpStatusCode < 600)
331-
|| err.code == NSURLErrorBadServerResponse
332-
|| err.code == NSURLErrorCannotConnectToHost
333-
|| err.code == NSURLErrorDNSLookupFailed
334-
|| err.code == NSURLErrorNotConnectedToInternet
335-
|| err.code == NSURLErrorTimedOut) {
336-
NSLog(@"Leanplum: %@", err);
337-
} else {
338-
id errorResponse = completedOperation.responseJSON;
339-
NSString *errorMessage = [LPResponse getResponseError:[LPResponse getLastResponse:errorResponse]];
340-
if (errorMessage) {
341-
if ([errorMessage hasPrefix:@"App not found"]) {
342-
errorMessage = @"No app matching the provided app ID was found.";
343-
constants.isInPermanentFailureState = YES;
344-
} else if ([errorMessage hasPrefix:@"Invalid access key"]) {
345-
errorMessage = @"The access key you provided is not valid for this app.";
346-
constants.isInPermanentFailureState = YES;
347-
} else if ([errorMessage hasPrefix:@"Development mode requested but not permitted"]) {
348-
errorMessage = @"A call to [Leanplum setAppIdForDevelopmentMode] with your production key was made, which is not permitted.";
349-
constants.isInPermanentFailureState = YES;
350-
}
351-
NSLog(@"Leanplum: %@", errorMessage);
352-
} else {
353-
NSLog(@"Leanplum: %@", err);
319+
// Retry on 500 and other network failures.
320+
NSInteger httpStatusCode = completedOperation.HTTPStatusCode;
321+
if (httpStatusCode == 408
322+
|| (httpStatusCode >= 500 && httpStatusCode < 600)
323+
|| err.code == NSURLErrorBadServerResponse
324+
|| err.code == NSURLErrorCannotConnectToHost
325+
|| err.code == NSURLErrorDNSLookupFailed
326+
|| err.code == NSURLErrorNotConnectedToInternet
327+
|| err.code == NSURLErrorTimedOut) {
328+
NSLog(@"Leanplum: %@", err);
329+
} else {
330+
id errorResponse = completedOperation.responseJSON;
331+
NSString *errorMessage = [LPResponse getResponseError:[LPResponse getLastResponse:errorResponse]];
332+
if (errorMessage) {
333+
if ([errorMessage hasPrefix:@"App not found"]) {
334+
errorMessage = @"No app matching the provided app ID was found.";
335+
constants.isInPermanentFailureState = YES;
336+
} else if ([errorMessage hasPrefix:@"Invalid access key"]) {
337+
errorMessage = @"The access key you provided is not valid for this app.";
338+
constants.isInPermanentFailureState = YES;
339+
} else if ([errorMessage hasPrefix:@"Development mode requested but not permitted"]) {
340+
errorMessage = @"A call to [Leanplum setAppIdForDevelopmentMode] with your production key was made, which is not permitted.";
341+
constants.isInPermanentFailureState = YES;
354342
}
355-
356-
// Delete on permanant error state.
357-
[LPEventDataManager deleteEventsWithLimit:requestsToSend.count];
343+
NSLog(@"Leanplum: %@", errorMessage);
344+
} else {
345+
NSLog(@"Leanplum: %@", err);
358346
}
359-
// Invoke errors on all requests.
360-
[LPEventCallbackManager invokeErrorCallbacksWithError:err];
361-
[[LPOperationQueue serialQueue] cancelAllOperations];
347+
348+
// Delete on permanant error state.
349+
[LPEventDataManager deleteEventsWithLimit:requestsToSend.count];
362350
}
351+
// Invoke errors on all requests.
352+
[LPEventCallbackManager invokeErrorCallbacksWithError:err];
353+
[[LPOperationQueue serialQueue] cancelAllOperations];
354+
363355
dispatch_semaphore_signal(semaphore);
364356
LP_END_TRY
365357
}];
@@ -412,23 +404,23 @@ - (void)sendEventually:(BOOL)sync
412404
_sent = YES;
413405

414406
void (^operationBlock)(void) = ^void() {
407+
LP_TRY
415408
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
416409
NSString *uuid = [userDefaults objectForKey:LEANPLUM_DEFAULTS_UUID_KEY];
417410
NSInteger count = [LPEventDataManager count];
418411
if (!uuid || count % MAX_EVENTS_PER_API_CALL == 0) {
419412
uuid = [LeanplumRequest generateUUID];
420413
}
421414

422-
@synchronized ([LPEventCallbackManager eventCallbackMap]) {
423-
NSMutableDictionary *args = [self createArgsDictionary];
424-
args[LP_PARAM_UUID] = uuid;
425-
426-
[LPEventDataManager addEvent:args];
415+
NSMutableDictionary *args = [self createArgsDictionary];
416+
args[LP_PARAM_UUID] = uuid;
417+
418+
[LPEventDataManager addEvent:args];
427419

428-
[LPEventCallbackManager addEventCallbackAt:count
429-
onSuccess:self->_response
430-
onError:self->_error];
431-
}
420+
[LPEventCallbackManager addEventCallbackAt:count
421+
onSuccess:self->_response
422+
onError:self->_error];
423+
LP_END_TRY
432424
};
433425

434426
if (sync) {

0 commit comments

Comments
 (0)