Search code examples
iosmultithreadingnsmutabledictionarygcdwebserver

Accessing mutable data from a GCDWebServer request handler


I'm getting occasional crashes in my GCDWebServer handlers, which access mutable dictionaries. The GCDWebServer ReadMe says the handlers "are executed on arbitrary threads within GCD so special attention must be paid to thread-safety and re-entrancy," and I think that's my problem. Is there a best practice or recommended pattern for accessing mutable properties of the parent object from the handlers?

I don't know if I can synchronize between threads since I'm not creating the handler threads. Also, I imagine I could use an asynchronous handler, then call a method on the main thread from there, then do my work in that method, then send the response, but that seems more complicated and less efficient than necessary.

Here's a simplified version of my code:

@property (nonatomic, strong) NSMutableDictionary *data;
@property (nonatomic, strong) GCDWebServer *webServer;

- (void)setup {
    self.data = [NSMutableDictionary dictionary];
    [self.data setObject:@"1" forKey:@"status"];

    self.webServer = [[GCDWebServer alloc] init];
    [self.webServer addHandlerForMethod:@"GET" path:@"/getStatus.txt" requestClass:[GCDWebServerRequest class] processBlock:^(GCDWebServerRequest *request) {
        return [self handleStatusRequest:request];
    }];
}

- (GCDWebServerDataResponse *)handleStatusRequest:(GCDWebServerRequest *)request {
    NSString *status = [self.data objectForKey:@"status"]; // crash here
    return [GCDWebServerDataResponse responseWithText:status];
}

Solution

  • Are you mutating your data dictionary after creating it? If so that would explain the crashes.

    You must prevent concurrent access to your data dictionary by using locks. The easiest way is through GCD e.g.

    @property dispatch_queue_t lock;
    
    __block NSString* status;
    dispatch_sync(self.lock, ^{
      status = [self.data objectForKey:@"status"];
    });
    
    NSString* status = @"Hello";
    dispatch_async(self.lock, ^{
      [self.data setObject:status forKey:@"status"];
    });  // Use dispatch_sync() or dispatch_async() here depending on your needs