// Copyright 1998-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#ifndef __sun__

#import "OFZone.h"

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>

RCS_ID("$Header: /Network/Developer/Source/CVS/OmniGroup/OmniFoundation/OFZone.m,v 1.7 1999/03/30 21:25:34 kc Exp $")

@implementation OFZone

static NSMapTable *zoneMap = NULL;
static struct mutex zoneMapLock;
static struct mutex fnordLock;

static unsigned int OFZoneSize = 0;
static unsigned int OFZoneGranularity = 0;

// #define DEBUG_ZONES
// #define NO_ZONES

#ifdef NO_ZONES
#define NSCreateZone(a,b,c) NSDefaultMallocZone()
#endif

+ (void)initialize
{
    if (!zoneMap) {
        zoneMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
                                   NSNonOwnedPointerMapValueCallBacks,
                                   10);
        mutex_init(&zoneMapLock);
        mutex_init(&fnordLock);

        if (!OFZoneSize)
            OFZoneSize = NSPageSize() * 4;
        if (!OFZoneGranularity)
            OFZoneGranularity = NSPageSize() * 2;
    }
}

+ zoneForNSZone:(NSZone *)aZone
{
    OFZone *retval;

    if (!aZone)
        return nil;

    mutex_lock(&zoneMapLock);
    retval = NSMapGet(zoneMap, aZone);

    if (!retval)
        retval = [[self allocWithZone:aZone] _initWithNSZone:aZone];
    else
        [retval retain];

    mutex_unlock(&zoneMapLock);

    return [retval autorelease];
}

/* This avoids a race condition in which the zone can be recycled before +zoneForNSZone: is called */
+ zoneForObject:(id <NSObject>)anObject
{
    OFZone *retval;
    NSZone *aZone;

    if (!anObject)
        return nil;

    mutex_lock(&zoneMapLock);
    aZone = [anObject zone];
    retval = NSMapGet(zoneMap, aZone);

    if (!retval)
        retval = [[self allocWithZone:aZone] _initWithNSZone:aZone];
    else
        [retval retain];

    mutex_unlock(&zoneMapLock);

    return [retval autorelease];
}    

+ defaultZone
{
    OFZone *retval;
    mutex_lock(&fnordLock);
    retval = [self zoneForNSZone:NSDefaultMallocZone()];
    mutex_unlock(&fnordLock);
    return retval;
}

+ new
{
    OFZone *retval;
    mutex_lock(&fnordLock);
    retval = [self zoneForNSZone:NSCreateZone(OFZoneSize, OFZoneGranularity, YES)];
    retval->ownsZone = YES;
    mutex_unlock(&fnordLock);
    return retval;
}

- init;
{
    [self autorelease];
    return nil;
}

/* Designated initializer. Assumes zoneMapLock is held. */
- _initWithNSZone:(NSZone *)aZone
{
    if (!(self = [super init]))
        return nil;

    zone = aZone;
    ownsZone = NO;
//    NSLog(@"Before: %@ thread=%@", NSAllMapTableValues(zoneMap), [NSThread currentThread]);
    NSMapInsertKnownAbsent(zoneMap, aZone, self);
//    NSLog(@"After: %@ thread=%@", NSAllMapTableValues(zoneMap), [NSThread currentThread]);
#ifdef DEBUG_ZONES
    NSLog(@"OFZone 0x%x created, thread=%@", (unsigned)zone, [NSThread currentThread]);
#endif

    return self;
}

- (void)dealloc
{
    [self recycle];
    [super dealloc];
}

/* TODO: Decide whether this is needed to avoid occasional disaster */
#if 0
- (void)release
{
    mutex_lock(&fnordLock);
    mutex_lock(&zoneMapLock);
    [super release];
    mutex_unlock(&zoneMapLock);
    mutex_unlock(&fnordLock);
}
#endif

- (void)recycle
{
    if (!zone)
        return;

    mutex_lock(&fnordLock);
    mutex_lock(&zoneMapLock);

#ifdef DEBUG_ZONES
    NSLog(@"OFZone 0x%x (%@) recycled", (unsigned)zone, [self name]);
#endif

    NSMapRemove(zoneMap, zone);
    if (ownsZone) {
#ifndef NO_ZONES
        NSRecycleZone(zone);
#endif
    }
    zone = NULL;
    
    mutex_unlock(&zoneMapLock);
    mutex_unlock(&fnordLock);

}

- (NSZone *)nsZone
{
    return zone;
}

- (void)setName:(NSString *)newName
{
    NSSetZoneName(zone, newName);
}

- (NSString *)name
{
    return NSZoneName(zone);
}

@end

#endif
