Radar5983285.m   [plain text]


/*
 * Copyright (c) 2009 Apple Inc. All rights reserved.
 *
 * @APPLE_APACHE_LICENSE_HEADER_START@
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * @APPLE_APACHE_LICENSE_HEADER_END@
 */
//
//  Radar5983285.m
//  auto
//
//  Created by Josh Behnke on 6/5/08.
//  Copyright 2008 Apple Inc. All rights reserved.
//

#import "Radar5983285.h"
#import <objc/objc-auto.h>

// simple test class with nontrivial layout
@interface Radar5983285_TestClass : NSObject
{
    id foo;
    int bar;
    id cat;
}
@end
@implementation Radar5983285_TestClass
@end

@implementation Radar5983285

@synthesize disguisedPointer;
@synthesize testThreadSynchronizer;
@synthesize collectorThreadSynchronizer;
@synthesize testBlockCollected;
@synthesize correctLayout;
@synthesize scannedWithBogusLayout;

- (void)startTest
{
    // allocate a test block and fetch its layout
    id testObject = [Radar5983285_TestClass new];
    auto_collection_control_t *control = auto_collection_parameters([self auto_zone]);
    self.correctLayout = control->layout_for_address([self auto_zone], testObject);
    [self setDisguisedPointer:[self disguise:testObject]];
    
    // keep a reference to the test block on the stack
    [self stackPointers][0] = testObject;
    
    // request a generational collection and go to sleep until our block gets pended
    self.testThreadSynchronizer = [self setNextTestSelector:@selector(blockWasPended)];
    [self requestGenerationalCollection];
}

- (void)blockWasPended
{
    if (_blockWasPended) {
        // The heap collector is about to pend our test block.
        // Clear our local reference and run a local collection.
        [self stackPointers][0] = nil;
        
        // allocate a bunch of objects to encourage the local collector to run
        int i;
        for (i=0; i<5000; i++)
            [NSObject new];
        
        // this runs a local collection
        [self requestCollection:AUTO_COLLECT_GENERATIONAL_COLLECTION|AUTO_COLLECT_IF_NEEDED];
        
        if (!self.testBlockCollected)
            [self fail:"test block was not collected as expected during local collection"];
        
        // now wake up the heap collector
        [self.collectorThreadSynchronizer signal];
        self.testThreadSynchronizer = [self setNextTestSelector:@selector(testResult)];
    } else {
        [self fail:"test block was not pended as expected"];
    }
}

- (void)testResult
{
    // check that our block was scanned with the correct layout
    if (self.scannedWithBogusLayout)
        [self fail:"block was scanned with invalid layout"];
}

- (void)heapCollectionComplete
{
    // just wake up test thread
    [self.testThreadSynchronizer signal];
    [super heapCollectionComplete];
}

- (void)setPending:(void *)block
{
    // wake up test thread once our block gets pended
    if (block == [self undisguise:self.disguisedPointer]) {
        _blockWasPended = YES;
        [self.testThreadSynchronizer signal];
        self.testThreadSynchronizer = nil;
        self.collectorThreadSynchronizer = [self setNextTestSelector:@selector(nop)];
    }
    [super setPending:block];
}

- (void)scanBlock:(void *)block endAddress:(void *)end
{
    // our block has layout info so should not be scanned conservatively
    if (block == [self undisguise:self.disguisedPointer]) {
        [self fail:"test block scanned without layout"];
    }
    [super scanBlock:block endAddress:end];
}

- (void)scanBlock:(void *)block endAddress:(void *)end withLayout:(const unsigned char *)map
{
    // verify the block's layout
    if (block == [self undisguise:self.disguisedPointer]) {
        if (map != self.correctLayout)
            self.scannedWithBogusLayout = YES;
    }
    [super scanBlock:block endAddress:end withLayout:map];
}

- (void)scanBlock:(void *)block endAddress:(void *)end withWeakLayout:(const unsigned char *)map
{
    // our block does not have weak layout
    if (block == [self undisguise:self.disguisedPointer]) {
        [self fail:"test block scanned with weak layout"];
    }
    [super scanBlock:block endAddress:end withWeakLayout:map];
}

- (void)endLocalScanWithGarbage:(void **)garbage_list count:(size_t)count
{
    // monitor for our test block becoming garbage
    if ([self block:[self undisguise:self.disguisedPointer] isInList:garbage_list count:count]) {
        self.testBlockCollected = YES;
    }
    [super endLocalScanWithGarbage:garbage_list count:count];
}

- (void)blockBecameGlobal:(void *)block withAge:(uint32_t)age
{
    // monitor if our test block becomes global (it should not)
    if (block == [self undisguise:self.disguisedPointer]) {
        [self fail:"test block became global unexpectedly"];
    }
    [super blockBecameGlobal:block withAge:age];
}

@end