Adium

Changeset 22436

Show
Ignore:
Timestamp:
01/20/2008 10:04:53 AM (10 months ago)
Author:
evands
Message:

Use a NSObject subclass rather than a malloc'd struct for SourceInfo, allowing real reference counting to take place easily. Pass CFRetain/CFRelease in the context information so that as the SourceInfo is passed around it's properly managed. This means we don't have to do manual tracking of the memory... and should fix any problems with the socket being retained elsewhere such that we free the SourceInfo before it should be freed.

Fixes #8692, so far as I can tell; thanks to tgos for detailed bug reporting and investigating of this issue which could cause seemingly random crashes. tgos, I can no longer reproduce the crash after this change; please let us know if that's true for you, as well.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Plugins/Purple Service/adiumPurpleEventloop.m

    r21519 r22436  
    2525 
    2626static guint                            sourceId = 0;           //The next source key; continuously incrementing 
    27 static NSMutableDictionary      *sourceInfoDict = nil; 
    2827static CFRunLoopRef                     purpleRunLoop = nil; 
    2928 
     
    3433                           void *infoVoid); 
    3534/* 
    36  * The sources, keyed by integer key id (wrapped in an NSValue), holding 
    37  * struct sourceInfo* values (wrapped in an NSValue). 
     35 * The sources, keyed by integer key id (wrapped in an NSNumber), holding 
     36 * SourceInfo * objects 
    3837 */ 
    39  
    40 // The structure of values of sourceInfoDict 
    41 struct SourceInfo { 
    42     CFSocketRef socket; 
    43     int fd; 
    44         CFRunLoopSourceRef run_loop_source; 
    45  
    46     guint timer_tag; 
    47         GSourceFunc timer_function; 
    48     CFRunLoopTimerRef timer; 
    49         gpointer timer_user_data; 
    50  
    51     guint read_tag; 
    52         PurpleInputFunction read_ioFunction; 
    53     gpointer read_user_data; 
    54  
    55         guint write_tag; 
    56         PurpleInputFunction write_ioFunction; 
    57     gpointer write_user_data; 
    58 }; 
    59  
    60 struct SourceInfo *newSourceInfo(void) 
    61 
    62         struct SourceInfo *info = (struct SourceInfo*)malloc(sizeof(struct SourceInfo)); 
     38static NSMutableDictionary      *sourceInfoDict = nil; 
     39 
     40/*! 
     41 * @class Holder for various source/timer information 
     42 * 
     43 * This serves as the context info for source and timer callbacks.  We use it just as a 
     44 * struct (declaring all the class's ivars to be public) but make it an object so we can use 
     45 * reference counting on it easily. 
     46 */ 
     47@interface SourceInfo : NSObject { 
     48 
     49@public CFSocketRef socket; 
     50@public int fd; 
     51@public CFRunLoopSourceRef run_loop_source; 
     52         
     53@public guint timer_tag; 
     54@public GSourceFunc timer_function; 
     55@public CFRunLoopTimerRef timer; 
     56@public gpointer timer_user_data; 
     57         
     58@public guint read_tag; 
     59@public PurpleInputFunction read_ioFunction; 
     60@public gpointer read_user_data; 
     61         
     62@public guint write_tag; 
     63@public PurpleInputFunction write_ioFunction; 
     64@public gpointer write_user_data;        
     65
     66@end 
     67 
     68@implementation SourceInfo 
     69- (NSString *)description 
     70
     71        return [NSString stringWithFormat:@"<SourceInfo %p: Socket %p: fd %i; timer_tag %i; read_tag %i; write_tag %i>", 
     72                        self, socket, fd, timer_tag, read_tag, write_tag]; 
     73
     74@end 
     75 
     76static SourceInfo *createSourceInfo(void) 
     77
     78        SourceInfo *info = [[SourceInfo alloc] init]; 
    6379 
    6480        info->socket = NULL; 
     
    92108 * This is necessary to prevent the now-unneeded condition from triggerring its callback. 
    93109 */ 
    94 void updateSocketForSourceInfo(struct SourceInfo *sourceInfo) 
     110void updateSocketForSourceInfo(SourceInfo *sourceInfo) 
    95111{ 
    96112        CFSocketRef socket = sourceInfo->socket; 
     
    121137 
    122138gboolean adium_source_remove(guint tag) { 
    123     struct SourceInfo *sourceInfo = (struct SourceInfo*) 
    124         [[sourceInfoDict objectForKey:[NSNumber numberWithUnsignedInt:tag]] pointerValue]; 
     139    SourceInfo *sourceInfo = (SourceInfo *)[sourceInfoDict objectForKey:[NSNumber numberWithUnsignedInt:tag]]; 
    125140 
    126141    if (sourceInfo) { 
     
    139154 
    140155                } 
    141                  
    142                 [sourceInfoDict removeObjectForKey:[NSNumber numberWithUnsignedInt:tag]]; 
    143156                 
    144157                if (sourceInfo->timer_tag == 0 && sourceInfo->read_tag == 0 && sourceInfo->write_tag == 0) { 
     
    161174                                CFRelease(sourceInfo->run_loop_source); 
    162175                        } 
    163  
    164                         free(sourceInfo); 
    165176                } else { 
    166177                        if ((sourceInfo->timer_tag == 0) && (sourceInfo->timer)) { 
     
    177188                        } 
    178189                } 
     190                 
     191                [sourceInfoDict removeObjectForKey:[NSNumber numberWithUnsignedInt:tag]]; 
    179192 
    180193                return TRUE; 
     
    193206void callTimerFunc(CFRunLoopTimerRef timer, void *info) 
    194207{ 
    195         struct SourceInfo *sourceInfo = info; 
     208        SourceInfo *sourceInfo = info; 
    196209 
    197210        if (!sourceInfo->timer_function || 
     
    203216guint adium_timeout_add(guint interval, GSourceFunc function, gpointer data) 
    204217{ 
    205     struct SourceInfo *info = newSourceInfo(); 
     218    SourceInfo *info = createSourceInfo(); 
    206219         
    207220        NSTimeInterval intervalInSec = (NSTimeInterval)interval/1000; 
    208         CFRunLoopTimerContext runLoopTimerContext = { 0, info, NULL, NULL, NULL }; 
     221         
     222        CFRunLoopTimerContext runLoopTimerContext = { 0, info, CFRetain, CFRelease, /* CFAllocatorCopyDescriptionCallBack */ NULL }; 
    209223        CFRunLoopTimerRef runLoopTimer = CFRunLoopTimerCreate( 
    210                                                                                                                   kCFAllocatorDefault, /* default allocator */ 
     224                                                                                                                  NULL, /* default allocator */ 
    211225                                                                                                                  (CFAbsoluteTimeGetCurrent() + intervalInSec), /* The time at which the timer should first fire */ 
    212226                                                                                                                  intervalInSec, /* firing interval */ 
     
    217231                                                                                                                  ); 
    218232        CFRunLoopAddTimer(purpleRunLoop, runLoopTimer, kCFRunLoopCommonModes); 
    219          
     233        [info release]; 
     234 
    220235        info->timer_function = function; 
    221236        info->timer = runLoopTimer; 
     
    223238        info->timer_tag = ++sourceId; 
    224239 
    225         [sourceInfoDict setObject:[NSValue valueWithPointer:info] 
     240        [sourceInfoDict setObject:info 
    226241                                           forKey:[NSNumber numberWithUnsignedInt:info->timer_tag]]; 
    227242 
     
    237252        } 
    238253 
    239     struct SourceInfo *info = newSourceInfo(); 
     254    SourceInfo *info = createSourceInfo(); 
    240255         
    241256    // And likewise the entire CFSocket 
    242     CFSocketContext context = { 0, info, /* CFAllocatorRetainCallBack */ NULL, /* CFAllocatorReleaseCallBack */ NULL, /* CFAllocatorCopyDescriptionCallBack */ NULL }; 
     257    CFSocketContext context = { 0, info, CFRetain, CFRelease, /* CFAllocatorCopyDescriptionCallBack */ NULL }; 
    243258 
    244259        /* 
     
    250265        AILog(@"adium_input_add(): Adding input %i on fd %i", condition, fd); 
    251266#endif 
    252         CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault
     267        CFSocketRef socket = CFSocketCreateWithNative(NULL
    253268                                                                                                  fd, 
    254269                                                                                                  (kCFSocketReadCallBack | kCFSocketWriteCallBack), 
     
    263278        CFSocketGetContext(socket, &actualSocketContext); 
    264279        if (actualSocketContext.info != info) { 
    265                 free(info)
     280                [info release]
    266281                CFRelease(socket); 
    267                 info = actualSocketContext.info
     282                info = [(SourceInfo *)(actualSocketContext.info) retain]
    268283        } 
    269284 
     
    276291                info->read_user_data = user_data; 
    277292                 
    278                 [sourceInfoDict setObject:[NSValue valueWithPointer:info] 
     293                [sourceInfoDict setObject:info 
    279294                                                   forKey:[NSNumber numberWithUnsignedInt:info->read_tag]]; 
    280295                 
     
    284299                info->write_user_data = user_data; 
    285300                 
    286                 [sourceInfoDict setObject:[NSValue valueWithPointer:info] 
     301                [sourceInfoDict setObject:info 
    287302                                                   forKey:[NSNumber numberWithUnsignedInt:info->write_tag]];             
    288303        } 
     
    292307        //Add it to our run loop 
    293308        if (!(info->run_loop_source)) { 
    294                 info->run_loop_source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0); 
     309                info->run_loop_source = CFSocketCreateRunLoopSource(NULL, socket, 0); 
    295310                if (info->run_loop_source) { 
    296311                        CFRunLoopAddSource(purpleRunLoop, info->run_loop_source, kCFRunLoopCommonModes); 
     
    299314                }                
    300315        } 
     316 
     317        [info release]; 
    301318 
    302319    return sourceId; 
     
    310327                                                   void *infoVoid) 
    311328{ 
    312     struct SourceInfo *sourceInfo = (struct SourceInfo*) infoVoid; 
     329    SourceInfo *sourceInfo = (SourceInfo *)infoVoid; 
    313330        gpointer user_data; 
    314331    PurpleInputCondition c;