cfclass.cpp   [plain text]


 /*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <security_utilities/cfclass.h>
#include <security_utilities/seccfobject.h>
#include <security_utilities/threading.h>
#include <CoreFoundation/CFString.h>
#include <sys/time.h>
#include <auto_zone.h>
#include <objc/objc-auto.h>

//
// CFClass
//
CFClass::CFClass(const char *name)
{
	// initialize the CFRuntimeClass structure
	version = 0;
	className = name;
	init = NULL;
	copy = NULL;
	finalize = finalizeType;
	equal = equalType;
	hash = hashType;
	copyFormattingDesc = copyFormattingDescType;
	copyDebugDesc = copyDebugDescType;
	
    // update because we are now doing our own reference counting
    version |= _kCFRuntimeCustomRefCount; // see ma, no hands!
    refcount = refCountForType;

	// register
	typeID = _CFRuntimeRegisterClass(this);
	assert(typeID != _kCFRuntimeNotATypeID);
}

uint32_t
CFClass::cleanupObject(intptr_t op, CFTypeRef cf, bool &zap)
{
    // the default is to not throw away the object
    zap = false;
    
    bool isGC = CF_IS_COLLECTABLE(cf);
    
    uint32_t currentCount;
    SecCFObject *obj = SecCFObject::optional(cf);

    uint32_t oldCount;
    currentCount = obj->updateRetainCount(op, &oldCount);
    
    if (isGC)
    {
        auto_zone_t* zone = objc_collectableZone();
        
        if (op == -1 && oldCount == 0)
        {
            auto_zone_release(zone, (void*) cf);
        }
        else if (op == 1 && oldCount == 0 && currentCount == 1)
        {
           auto_zone_retain(zone, (void*) cf);
        }
        else if (op == -1 && oldCount == 1 && currentCount == 0)
        {
            /*
                To prevent accidental resurrection, just pull it out of the
                cache.
            */
            obj->aboutToDestruct();
            auto_zone_release(zone, (void*) cf);
        }
        else if (op == 0)
        {
            return currentCount;
        }
        
        return 0;
    }

    if (op == 0)
    {
        return currentCount;
    }
    else if (currentCount == 0)
    {
        finalizeType(cf);
        zap = true; // the the caller to release the mutex and zap the object
        return 0;
    }
    else 
    {
        return 0;
    }
}

uint32_t
CFClass::refCountForType(intptr_t op, CFTypeRef cf) throw()
{
    uint32_t result = 0;
    bool zap = false;

    try
    {
        SecCFObject *obj = SecCFObject::optional(cf);
		Mutex* mutex = obj->getMutexForObject();
		if (mutex == NULL)
		{
			// if the object didn't have a mutex, it wasn't cached.
			// Just clean it up and get out.
            result = cleanupObject(op, cf, zap);
		}
		else
        {
            // we have a mutex, so we need to do our cleanup operation under its control
            StLock<Mutex> _(*mutex);
            result = cleanupObject(op, cf, zap);
        }
        
        if (zap) // did we release the object?
        {
            delete obj; // should call the overloaded delete for the object
        }
    }
    catch (...)
    {
    }
    
    // keep the compiler happy
    return result;
}



void
CFClass::finalizeType(CFTypeRef cf) throw()
{
    /*
        Why are we asserting the mutex here as well as in refCountForType?
        Because the way we control the objects and the queues are different
        under GC than they are under non-GC operations.
        
        In non-GC, we need to control the lifetime of the object.  This means
        that the cache lock has to be asserted while we are determining if the
        object should live or die.  The mutex is recursive, which means that
        we won't end up with mutex inversion.
        
        In GC, GC figures out the lifetime of the object.  We probably don't need
        to assert the mutex here, but it doesn't hurt.
    */
    
    SecCFObject *obj = SecCFObject::optional(cf);

	bool isCollectable = CF_IS_COLLECTABLE(cf);

    try
	{
		Mutex* mutex = obj->getMutexForObject();
		if (mutex == NULL)
		{
			// if the object didn't have a mutex, it wasn't cached.
			// Just clean it up and get out.
			obj->aboutToDestruct(); // removes the object from its associated cache.
		}
		else
        {
            StLock<Mutex> _(*mutex);
            
            if (obj->isNew())
            {
                // New objects aren't in the cache.
                // Just clean it up and get out.
                obj->aboutToDestruct(); // removes the object from its associated cache.
                return;
            }
            
            obj->aboutToDestruct(); // removes the object from its associated cache.
        }
	}
	catch(...)
	{
	}
    
    if (isCollectable)
    {
        delete obj;
    }
}

Boolean
CFClass::equalType(CFTypeRef cf1, CFTypeRef cf2) throw()
{
	// CF checks for pointer equality and ensures type equality already
	try {
		return SecCFObject::optional(cf1)->equal(*SecCFObject::optional(cf2));
	} catch (...) {
		return false;
	}
}

CFHashCode
CFClass::hashType(CFTypeRef cf) throw()
{
	try {
		return SecCFObject::optional(cf)->hash();
	} catch (...) {
		return 666; /* Beasty return for error */
	}
}

CFStringRef
CFClass::copyFormattingDescType(CFTypeRef cf, CFDictionaryRef dict) throw()
{
	try {
		return SecCFObject::optional(cf)->copyFormattingDesc(dict);
	} catch (...) {
		return CFSTR("Exception thrown trying to format object");
	}
}

CFStringRef
CFClass::copyDebugDescType(CFTypeRef cf) throw()
{
	try {
		return SecCFObject::optional(cf)->copyDebugDesc();
	} catch (...) {
		return CFSTR("Exception thrown trying to format object");
	}
}