Deep recursion in deinit should not happen

Same issue in Objective-C
#import <Foundation/Foundation.h>

@interface Chained: NSObject
@property Chained* next;
@end

@implementation Chained
- (void)dealloc {
    static int n = 0;
    if ((n % 1000) == 0) {
        NSLog(@"dealloc %d", n);
    }
    n += 1;
}
@end

void test(void) {
    NSLog(@"building...");
    Chained* first = [Chained new];
    Chained* last = first;
    int n = 100000; // 1000000 for release mode
    for (int i = 1; i < n; i++) {
        Chained* next = [Chained new];
        last.next = next;
        last = next;
    }
    last = nil;
    
    NSLog(@"forgetting references...");
    first = nil;
    
    /*
     Thread sanitizer, running outside Xcode
    2022-02-02 00:20:23.496 StackOverflow[15381:446319] dealloc 30000
    2022-02-02 00:20:24.229 StackOverflow[15381:446319] dealloc 31000
    2022-02-02 00:20:24.980 StackOverflow[15381:446319] dealloc 32000
    ThreadSanitizer:DEADLYSIGNAL
    ==15381==ERROR: ThreadSanitizer: stack-overflow on address 0x7ffee22edff8 (pc 0x00010d13a21c bp 0x7ffee22ee020 sp 0x7ffee22ee000 T446319)
    */
    
    /*
     No thread sanitizer:
     2022-02-02 00:21:45.604 StackOverflow[15425:447543] dealloc 38000
     2022-02-02 00:21:45.605 StackOverflow[15425:447543] dealloc 39000
     2022-02-02 00:21:45.605 StackOverflow[15425:447543] dealloc 40000
     Segmentation fault: 11
     */
    
    /*
     release mode:
     2022-02-02 00:22:29.130 StackOverflow[15478:448365] dealloc 45000
     2022-02-02 00:22:29.130 StackOverflow[15478:448365] dealloc 46000
     2022-02-02 00:22:29.130 StackOverflow[15478:448365] dealloc 47000
     Segmentation fault: 11
     */
    NSLog(@"done");
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
    }
    return 0;
}
Ditto for the manual reference counted version (different number of deallocations until crash)
specify -fno-objc-arc for this file in build phases

#import <Foundation/Foundation.h>

@interface Chained: NSObject {
    @public
    Chained* next;
}
@end

@implementation Chained
- (void)dealloc {
    static int n = 0;
    if ((n % 1000) == 0) {
        NSLog(@"dealloc %d", n);
    }
    n += 1;
    Chained* next = self->next;
    [super dealloc];
    [next release];
}
@end

void test(void) {
    NSLog(@"building...");
    Chained* first = [Chained new];
    Chained* last = first;
    int n = 1000000;
    for (int i = 1; i < n; i++) {
        Chained* next = [Chained new];
        last->next = next;
        last = next;
    }
    last = nil;
    
    NSLog(@"forgetting references...");
    [first release];
    
    /*
     Thread sanitizer, running outside Xcode
     2022-02-02 00:38:59.498 StackOverflow[15633:461353] dealloc 67000
     2022-02-02 00:38:59.922 StackOverflow[15633:461353] dealloc 68000
     2022-02-02 00:39:00.358 StackOverflow[15633:461353] dealloc 69000
     2022-02-02 00:39:00.808 StackOverflow[15633:461353] dealloc 70000
     ThreadSanitizer:DEADLYSIGNAL
     ThreadSanitizer:DEADLYSIGNAL
     ThreadSanitizer: nested bug in the same thread, aborting.
    */
    
    /*
     No thread sanitizer:
     2022-02-02 00:40:15.630 StackOverflow[15722:463283] dealloc 126000
     2022-02-02 00:40:15.630 StackOverflow[15722:463283] dealloc 127000
     2022-02-02 00:40:15.630 StackOverflow[15722:463283] dealloc 128000
     2022-02-02 00:40:15.630 StackOverflow[15722:463283] dealloc 129000
     2022-02-02 00:40:15.631 StackOverflow[15722:463283] dealloc 130000
     Segmentation fault: 11
     */
    
    /*
     release mode:
     2022-02-02 00:40:47.407 StackOverflow[15765:463962] dealloc 171000
     2022-02-02 00:40:47.407 StackOverflow[15765:463962] dealloc 172000
     2022-02-02 00:40:47.408 StackOverflow[15765:463962] dealloc 173000
     2022-02-02 00:40:47.408 StackOverflow[15765:463962] dealloc 174000
     Segmentation fault: 11
     */
    NSLog(@"done");
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
    }
    return 0;
}

I wonder how did we not notice it before.. No Objective-C user was using linked lists with 30-100K elements in the last 38 years? :thinking:

4 Likes