CFPlugIn_Factory.c   [plain text]


/*
 * Copyright (c) 2014 Apple 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@
 */

/*	CFPlugIn_Factory.c
	Copyright (c) 1999-2013, Apple Inc.  All rights reserved.
        Responsibility: Tony Parker
*/

#include "CFBundle_Internal.h"
#include "CFInternal.h"

static CFTypeID __kCFPFactoryTypeID = _kCFRuntimeNotATypeID;

struct __CFPFactory {
    CFRuntimeBase _base;
        
    CFUUIDRef _uuid;
    Boolean _enabled;
    char _padding[3];
    
    CFPlugInFactoryFunction _func;
    
    CFPlugInRef _plugIn;
    CFStringRef _funcName;
    
    CFMutableArrayRef _types;
    CFSpinLock_t _lock;
};

static void _CFPFactoryDeallocate(CFTypeRef factory);

static const CFRuntimeClass __CFPFactoryClass = {
    0,
    "_CFPFactory",
    NULL,	// init
    NULL,	// copy
    _CFPFactoryDeallocate,
    NULL,	// equal
    NULL,	// hash
    NULL,       // formatting desc
    NULL,       // debug desc
};

CF_PRIVATE void __CFPFactoryInitialize(void) {
    __kCFPFactoryTypeID = _CFRuntimeRegisterClass(&__CFPFactoryClass);
}

static CFTypeID _CFPFactoryGetTypeID(void) {
    return __kCFPFactoryTypeID;
}

static CFSpinLock_t CFPlugInGlobalDataLock = CFSpinLockInit;
static CFMutableDictionaryRef _factoriesByFactoryID = NULL; /* Value is _CFPFactoryRef */
static CFMutableDictionaryRef _factoriesByTypeID = NULL; /* Value is array of _CFPFactoryRef */

static void _CFPFactoryAddToTable(_CFPFactoryRef factory) {
    __CFSpinLock(&factory->_lock);
    CFUUIDRef uuid = (CFUUIDRef)CFRetain(factory->_uuid);
    CFRetain(factory);
    __CFSpinUnlock(&factory->_lock);
    
    __CFSpinLock(&CFPlugInGlobalDataLock);    
    if (!_factoriesByFactoryID) {
        CFDictionaryValueCallBacks _factoryDictValueCallbacks = {0, NULL, NULL, NULL, NULL};
        _factoriesByFactoryID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &_factoryDictValueCallbacks);
    }
    CFDictionarySetValue(_factoriesByFactoryID, uuid, factory);
    __CFSpinUnlock(&CFPlugInGlobalDataLock);
    
    if (uuid) CFRelease(uuid);
    CFRelease(factory);
}

static void _CFPFactoryRemoveFromTable(_CFPFactoryRef factory) {
    __CFSpinLock(&factory->_lock);
    CFUUIDRef uuid = factory->_uuid;
    if (uuid) CFRetain(uuid);
    __CFSpinUnlock(&factory->_lock);
    
    __CFSpinLock(&CFPlugInGlobalDataLock);
    if (uuid && _factoriesByTypeID) CFDictionaryRemoveValue(_factoriesByFactoryID, uuid);
    __CFSpinUnlock(&CFPlugInGlobalDataLock);
    
    if (uuid) CFRelease(uuid);
}

CF_PRIVATE _CFPFactoryRef _CFPFactoryFind(CFUUIDRef factoryID, Boolean enabled) {
    _CFPFactoryRef result = NULL;
    
    __CFSpinLock(&CFPlugInGlobalDataLock);
    if (_factoriesByFactoryID) {
        result = (_CFPFactoryRef )CFDictionaryGetValue(_factoriesByFactoryID, factoryID);
        if (result && result->_enabled != enabled) result = NULL;
    }
    __CFSpinUnlock(&CFPlugInGlobalDataLock);
    return result;
}

static void _CFPFactoryDeallocate(CFTypeRef ty) {
    SInt32 c;
    _CFPFactoryRef factory = (_CFPFactoryRef)ty;
    
    _CFPFactoryRemoveFromTable(factory);

    if (factory->_plugIn) {
        _CFPlugInRemoveFactory(factory->_plugIn, factory);
        CFRelease(factory->_plugIn);
    }
    
    /* Remove all types for this factory. */
    c = CFArrayGetCount(factory->_types);
    while (c-- > 0) _CFPFactoryRemoveType(factory, (CFUUIDRef)CFArrayGetValueAtIndex(factory->_types, c));
    CFRelease(factory->_types);

    if (factory->_funcName) CFRelease(factory->_funcName);
    if (factory->_uuid) CFRelease(factory->_uuid);
}

static _CFPFactoryRef _CFPFactoryCommonCreate(CFAllocatorRef allocator, CFUUIDRef factoryID) {
    _CFPFactoryRef factory;
    uint32_t size;
    size = sizeof(struct __CFPFactory) - sizeof(CFRuntimeBase);
    factory = (_CFPFactoryRef)_CFRuntimeCreateInstance(allocator, _CFPFactoryGetTypeID(), size, NULL);
    if (!factory) return NULL;

    factory->_uuid = (CFUUIDRef)CFRetain(factoryID);
    factory->_enabled = true;
    factory->_types = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
    factory->_lock = CFSpinLockInit; // WARNING: grab global lock before this lock
        
    _CFPFactoryAddToTable(factory);

    return factory;
}

CF_PRIVATE _CFPFactoryRef _CFPFactoryCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInFactoryFunction func) {
    _CFPFactoryRef factory = _CFPFactoryCommonCreate(allocator, factoryID);

    __CFSpinLock(&factory->_lock);    
    factory->_func = func;
    factory->_plugIn = NULL;
    factory->_funcName = NULL;
    __CFSpinUnlock(&factory->_lock);

    return factory;
}

CF_PRIVATE _CFPFactoryRef _CFPFactoryCreateByName(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef funcName) {
    _CFPFactoryRef factory = _CFPFactoryCommonCreate(allocator, factoryID);

    __CFSpinLock(&factory->_lock);    
    factory->_func = NULL;
    factory->_plugIn = (CFPlugInRef)CFRetain(plugIn);
    if (plugIn) _CFPlugInAddFactory(plugIn, factory);
    factory->_funcName = (funcName ? (CFStringRef)CFStringCreateCopy(allocator, funcName) : NULL);
    __CFSpinUnlock(&factory->_lock);

    return factory;
}

CF_PRIVATE CFUUIDRef _CFPFactoryCopyFactoryID(_CFPFactoryRef factory) {
    __CFSpinLock(&factory->_lock);
    CFUUIDRef uuid = factory->_uuid;
    if (uuid) CFRetain(uuid);
    __CFSpinUnlock(&factory->_lock);
    return uuid;
}

CF_PRIVATE CFPlugInRef _CFPFactoryCopyPlugIn(_CFPFactoryRef factory) {
    __CFSpinLock(&factory->_lock);
    CFPlugInRef result = factory->_plugIn;
    if (result) CFRetain(result);
    __CFSpinUnlock(&factory->_lock);
    return result;
}

CF_PRIVATE void *_CFPFactoryCreateInstance(CFAllocatorRef allocator, _CFPFactoryRef factory, CFUUIDRef typeID) {
    void *result = NULL;

    __CFSpinLock(&factory->_lock);
    if (factory->_enabled) {
        if (!factory->_func) {
            factory->_func = (CFPlugInFactoryFunction)CFBundleGetFunctionPointerForName(factory->_plugIn, factory->_funcName);
            if (!factory->_func) CFLog(__kCFLogPlugIn, CFSTR("Cannot find function pointer %@ for factory %@ in %@"), factory->_funcName, factory->_uuid, factory->_plugIn);
        }
        if (factory->_func) {
            // UPPGOOP
            CFPlugInFactoryFunction f = factory->_func;
            __CFSpinUnlock(&factory->_lock);
            FAULT_CALLBACK((void **)&(f));
            result = (void *)INVOKE_CALLBACK2(f, allocator, typeID);
            __CFSpinLock(&factory->_lock);
        }
    } else {
        CFLog(__kCFLogPlugIn, CFSTR("Factory %@ is disabled"), factory->_uuid);
    }    
    __CFSpinUnlock(&factory->_lock);

    return result;
}

CF_PRIVATE void _CFPFactoryDisable(_CFPFactoryRef factory) {
    __CFSpinLock(&factory->_lock);    
    factory->_enabled = false;
    __CFSpinUnlock(&factory->_lock);
    CFRelease(factory);
}

CF_PRIVATE void _CFPFactoryFlushFunctionCache(_CFPFactoryRef factory) {
    /* MF:!!! Assert that this factory belongs to a plugIn. */
    /* This is called by the factory's plugIn when the plugIn unloads its code. */
    __CFSpinLock(&factory->_lock);
    factory->_func = NULL;
    __CFSpinUnlock(&factory->_lock);
}

CF_PRIVATE void _CFPFactoryAddType(_CFPFactoryRef factory, CFUUIDRef typeID) {
    /* Add the factory to the type's array of factories */
    __CFSpinLock(&factory->_lock);
    /* Add the type to the factory's type list */
    CFArrayAppendValue(factory->_types, typeID);
    __CFSpinUnlock(&factory->_lock);

    __CFSpinLock(&CFPlugInGlobalDataLock);
    if (!_factoriesByTypeID) _factoriesByTypeID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
    if (!array) {
        CFArrayCallBacks _factoryArrayCallbacks = {0, NULL, NULL, NULL, NULL};
        // Create this from default allocator
        array = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &_factoryArrayCallbacks);
        CFDictionarySetValue(_factoriesByTypeID, typeID, array);
        CFRelease(array);
    }
    CFArrayAppendValue(array, factory);
    __CFSpinUnlock(&CFPlugInGlobalDataLock);
}

CF_PRIVATE void _CFPFactoryRemoveType(_CFPFactoryRef factory, CFUUIDRef typeID) {
    /* Remove it from the factory's type list */
    SInt32 idx;

    __CFSpinLock(&factory->_lock);
    idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID);
    if (idx >= 0) CFArrayRemoveValueAtIndex(factory->_types, idx);
    __CFSpinUnlock(&factory->_lock);

    /* Remove the factory from the type's list of factories */
    __CFSpinLock(&CFPlugInGlobalDataLock);
    if (_factoriesByTypeID) {
        CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
        if (array) {
            idx = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), factory);
            if (idx >= 0) {
                CFArrayRemoveValueAtIndex(array, idx);
                if (CFArrayGetCount(array) == 0) CFDictionaryRemoveValue(_factoriesByTypeID, typeID);
            }
        }
    }
    __CFSpinUnlock(&CFPlugInGlobalDataLock);
}

CF_PRIVATE Boolean _CFPFactorySupportsType(_CFPFactoryRef factory, CFUUIDRef typeID) {
    SInt32 idx;

    __CFSpinLock(&factory->_lock);
    idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID);
    __CFSpinUnlock(&factory->_lock);
    
    return (idx >= 0 ? true : false);
}

CF_PRIVATE CFArrayRef _CFPFactoryFindCopyForType(CFUUIDRef typeID) {
    CFArrayRef result = NULL;
    __CFSpinLock(&CFPlugInGlobalDataLock);
    if (_factoriesByTypeID) {
        result = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
        if (result) CFRetain(result);
    }
    __CFSpinUnlock(&CFPlugInGlobalDataLock);

    return result;
}

/* These methods are called by CFPlugInInstance when an instance is created or destroyed.  If a factory's instance count goes to 0 and the factory has been disabled, the factory is destroyed. */
CF_PRIVATE void _CFPFactoryAddInstance(_CFPFactoryRef factory) {
    /* MF:!!! Assert that factory is enabled. */
    CFRetain(factory);
    __CFSpinLock(&factory->_lock);
    CFPlugInRef plugin = factory->_plugIn;
    if (plugin) CFRetain(plugin);
    __CFSpinUnlock(&factory->_lock);
    if (plugin) {
        _CFPlugInAddPlugInInstance(plugin);
        CFRelease(plugin);
    }    
}

CF_PRIVATE void _CFPFactoryRemoveInstance(_CFPFactoryRef factory) {
    __CFSpinLock(&factory->_lock);
    CFPlugInRef plugin = factory->_plugIn;
    if (plugin) CFRetain(plugin);
    __CFSpinUnlock(&factory->_lock);
    if (plugin) {
        _CFPlugInRemovePlugInInstance(factory->_plugIn);
        CFRelease(plugin);
    }
    CFRelease(factory);
}