Kaynağa Gözat

Improve thread safety in the network recorder.

The recorder methods can be called from any thread, so we need to be careful around the internal mutable structures that are not thread safe. The bulk of this diff is from indentation changes.
Ryan Olson 11 yıl önce
ebeveyn
işleme
a53f744035
1 değiştirilmiş dosya ile 63 ekleme ve 46 silme
  1. 63 46
      Classes/Network/FLEXNetworkRecorder.m

+ 63 - 46
Classes/Network/FLEXNetworkRecorder.m

@@ -20,6 +20,7 @@ NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction";
 @property (nonatomic, strong) NSCache *responseCache;
 @property (nonatomic, strong) NSMutableArray *orderedTransactions;
 @property (nonatomic, strong) NSMutableDictionary *networkTransactionsForRequestIdentifiers;
+@property (nonatomic, strong) dispatch_queue_t queue;
 
 @end
 
@@ -32,6 +33,7 @@ NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction";
         self.responseCache = [[NSCache alloc] init];
         self.orderedTransactions = [NSMutableArray array];
         self.networkTransactionsForRequestIdentifiers = [NSMutableDictionary dictionary];
+        self.queue = dispatch_queue_create("com.flex.FLEXNetworkRecorder", DISPATCH_QUEUE_CONCURRENT);
     }
     return self;
 }
@@ -50,7 +52,11 @@ NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction";
 
 - (NSArray *)networkTransactions
 {
-    return [self.orderedTransactions copy];
+    __block NSArray *transactions = nil;
+    dispatch_sync(self.queue, ^{
+        transactions = [self.orderedTransactions copy];
+    });
+    return transactions;
 }
 
 - (NSData *)cachedResponseBodyForTransaction:(FLEXNetworkTransaction *)transaction
@@ -62,73 +68,84 @@ NSString *const kFLEXNetworkRecorderUserInfoTransactionKey = @"transaction";
 
 - (void)recordRequestWillBeSentWithRequestId:(NSString *)requestId request:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
 {
-    // FIXME (RKO): What to do with the redirect response?
+    // Use a barrier here because we mutate collections that are not thread safe.
+    dispatch_barrier_async(self.queue, ^{
+        // FIXME (RKO): What to do with the redirect response?
 
-    FLEXNetworkTransaction *transaction = [[FLEXNetworkTransaction alloc] init];
-    transaction.requestId = requestId;
-    transaction.request = request;
-    transaction.startTime = [NSDate date];
-    transaction.transactionState = FLEXNetworkTransactionStateAwaitingResponse;
+        FLEXNetworkTransaction *transaction = [[FLEXNetworkTransaction alloc] init];
+        transaction.requestId = requestId;
+        transaction.request = request;
+        transaction.startTime = [NSDate date];
+        transaction.transactionState = FLEXNetworkTransactionStateAwaitingResponse;
 
-    [self.orderedTransactions addObject:transaction];
-    [self.networkTransactionsForRequestIdentifiers setObject:transaction forKey:requestId];
+        [self.orderedTransactions addObject:transaction];
+        [self.networkTransactionsForRequestIdentifiers setObject:transaction forKey:requestId];
 
-    [self postNewTransactionNotificationWithTransaction:transaction];
+        [self postNewTransactionNotificationWithTransaction:transaction];
+    });
 }
 
 - (void)recordResponseReceivedWithRequestId:(NSString *)requestId response:(NSURLResponse *)response
 {
-    FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
-    transaction.response = response;
-    transaction.transactionState = FLEXNetworkTransactionStateReceivingData;
-    transaction.latency = -[transaction.startTime timeIntervalSinceNow];
+    dispatch_async(self.queue, ^{
+        FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
+        transaction.response = response;
+        transaction.transactionState = FLEXNetworkTransactionStateReceivingData;
+        transaction.latency = -[transaction.startTime timeIntervalSinceNow];
 
-    [self postUpdateNotificationForTransaction:transaction];
+        [self postUpdateNotificationForTransaction:transaction];
+    });
 }
 
 - (void)recordDataReceivedWithRequestId:(NSString *)requestId dataLength:(int64_t)dataLength
 {
-    FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
-    transaction.receivedDataLength += dataLength;
+    dispatch_async(self.queue, ^{
+        FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
+        transaction.receivedDataLength += dataLength;
 
-    [self postUpdateNotificationForTransaction:transaction];
+        [self postUpdateNotificationForTransaction:transaction];
+    });
 }
 
 - (void)recordLoadingFinishedWithRequestId:(NSString *)requestId responseBody:(NSData *)responseBody
 {
-    FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
-    transaction.transactionState = FLEXNetworkTransactionStateFinished;
-    transaction.duration = -[transaction.startTime timeIntervalSinceNow];
-
-    [self.responseCache setObject:responseBody forKey:requestId cost:[responseBody length]];
-
-    NSString *mimeType = transaction.response.MIMEType;
-    if ([mimeType hasPrefix:@"image/"]) {
-        // Thumbnail image previews on a background queue
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            NSInteger maxPixelDimension = [[UIScreen mainScreen] scale] * 32.0;
-            transaction.responseThumbnail = [FLEXUtility thumbnailedImageWithMaxPixelDimension:maxPixelDimension fromImageData:responseBody];
-            [self postUpdateNotificationForTransaction:transaction];
-        });
-    } else if ([mimeType isEqual:@"application/json"]) {
-        transaction.responseThumbnail = [FLEXResources jsonIcon];
-    } else if ([mimeType isEqual:@"text/plain"]){
-        transaction.responseThumbnail = [FLEXResources textPlainIcon];
-    } else if ([mimeType isEqual:@"text/html"]) {
-        transaction.responseThumbnail = [FLEXResources htmlIcon];
-    }
-
-    [self postUpdateNotificationForTransaction:transaction];
+    dispatch_async(self.queue, ^{
+        FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
+        transaction.transactionState = FLEXNetworkTransactionStateFinished;
+        transaction.duration = -[transaction.startTime timeIntervalSinceNow];
+
+        [self.responseCache setObject:responseBody forKey:requestId cost:[responseBody length]];
+
+        NSString *mimeType = transaction.response.MIMEType;
+        if ([mimeType hasPrefix:@"image/"]) {
+            // Thumbnail image previews on a separate background queue
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                NSInteger maxPixelDimension = [[UIScreen mainScreen] scale] * 32.0;
+                transaction.responseThumbnail = [FLEXUtility thumbnailedImageWithMaxPixelDimension:maxPixelDimension fromImageData:responseBody];
+                [self postUpdateNotificationForTransaction:transaction];
+            });
+        } else if ([mimeType isEqual:@"application/json"]) {
+            transaction.responseThumbnail = [FLEXResources jsonIcon];
+        } else if ([mimeType isEqual:@"text/plain"]){
+            transaction.responseThumbnail = [FLEXResources textPlainIcon];
+        } else if ([mimeType isEqual:@"text/html"]) {
+            transaction.responseThumbnail = [FLEXResources htmlIcon];
+        }
+        
+        [self postUpdateNotificationForTransaction:transaction];
+    });
 }
 
 - (void)recordLoadingFailedWithRequestId:(NSString *)requestId error:(NSError *)error
 {
-    FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
-    transaction.transactionState = FLEXNetworkTransactionStateFailed;
-    transaction.duration = [transaction.startTime timeIntervalSinceNow];
-    transaction.error = error;
+    dispatch_async(self.queue, ^{
+        FLEXNetworkTransaction *transaction = [self.networkTransactionsForRequestIdentifiers objectForKey:requestId];
+        transaction.transactionState = FLEXNetworkTransactionStateFailed;
+        transaction.duration = [transaction.startTime timeIntervalSinceNow];
+        transaction.error = error;
 
-    [self postUpdateNotificationForTransaction:transaction];
+        [self postUpdateNotificationForTransaction:transaction];
+    });
 }
 
 - (void)postNewTransactionNotificationWithTransaction:(FLEXNetworkTransaction *)transaction