1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <security_utilities/cfclass.h>
25#include <security_utilities/seccfobject.h>
26#include <security_utilities/threading.h>
27#include <CoreFoundation/CFString.h>
28#include <sys/time.h>
29#include <auto_zone.h>
30#include <objc/objc-auto.h>
31
32//
33// CFClass
34//
35CFClass::CFClass(const char *name)
36{
37	// initialize the CFRuntimeClass structure
38	version = 0;
39	className = name;
40	init = NULL;
41	copy = NULL;
42	finalize = finalizeType;
43	equal = equalType;
44	hash = hashType;
45	copyFormattingDesc = copyFormattingDescType;
46	copyDebugDesc = copyDebugDescType;
47
48    // update because we are now doing our own reference counting
49    version |= _kCFRuntimeCustomRefCount; // see ma, no hands!
50    refcount = refCountForType;
51
52	// register
53	typeID = _CFRuntimeRegisterClass(this);
54	assert(typeID != _kCFRuntimeNotATypeID);
55}
56
57uint32_t
58CFClass::cleanupObject(intptr_t op, CFTypeRef cf, bool &zap)
59{
60    // the default is to not throw away the object
61    zap = false;
62
63    bool isGC = CF_IS_COLLECTABLE(cf);
64
65    uint32_t currentCount;
66    SecCFObject *obj = SecCFObject::optional(cf);
67
68    uint32_t oldCount;
69    currentCount = obj->updateRetainCount(op, &oldCount);
70
71    if (isGC)
72    {
73        auto_zone_t* zone = objc_collectableZone();
74
75        if (op == -1 && oldCount == 0)
76        {
77            auto_zone_release(zone, (void*) cf);
78        }
79        else if (op == 1 && oldCount == 0 && currentCount == 1)
80        {
81           auto_zone_retain(zone, (void*) cf);
82        }
83        else if (op == -1 && oldCount == 1 && currentCount == 0)
84        {
85            /*
86                To prevent accidental resurrection, just pull it out of the
87                cache.
88            */
89            obj->aboutToDestruct();
90            auto_zone_release(zone, (void*) cf);
91        }
92        else if (op == 0)
93        {
94            return currentCount;
95        }
96
97        return 0;
98    }
99
100    if (op == 0)
101    {
102        return currentCount;
103    }
104    else if (currentCount == 0)
105    {
106        // we may not be able to delete if the caller has active children
107        if (obj->mayDelete())
108        {
109            finalizeType(cf);
110            zap = true; // ask the caller to release the mutex and zap the object
111            return 0;
112        }
113        else
114        {
115            return currentCount;
116        }
117    }
118    else
119    {
120        return 0;
121    }
122}
123
124uint32_t
125CFClass::refCountForType(intptr_t op, CFTypeRef cf) throw()
126{
127    uint32_t result = 0;
128    bool zap = false;
129
130    try
131    {
132        SecCFObject *obj = SecCFObject::optional(cf);
133		Mutex* mutex = obj->getMutexForObject();
134		if (mutex == NULL)
135		{
136			// if the object didn't have a mutex, it wasn't cached.
137			// Just clean it up and get out.
138            result = cleanupObject(op, cf, zap);
139		}
140		else
141        {
142            // we have a mutex, so we need to do our cleanup operation under its control
143            StLock<Mutex> _(*mutex);
144            result = cleanupObject(op, cf, zap);
145        }
146
147        if (zap) // did we release the object?
148        {
149            delete obj; // should call the overloaded delete for the object
150        }
151    }
152    catch (...)
153    {
154    }
155
156    // keep the compiler happy
157    return result;
158}
159
160
161
162void
163CFClass::finalizeType(CFTypeRef cf) throw()
164{
165    /*
166        Why are we asserting the mutex here as well as in refCountForType?
167        Because the way we control the objects and the queues are different
168        under GC than they are under non-GC operations.
169
170        In non-GC, we need to control the lifetime of the object.  This means
171        that the cache lock has to be asserted while we are determining if the
172        object should live or die.  The mutex is recursive, which means that
173        we won't end up with mutex inversion.
174
175        In GC, GC figures out the lifetime of the object.  We probably don't need
176        to assert the mutex here, but it doesn't hurt.
177    */
178
179    SecCFObject *obj = SecCFObject::optional(cf);
180
181	bool isCollectable = CF_IS_COLLECTABLE(cf);
182
183    try
184	{
185		Mutex* mutex = obj->getMutexForObject();
186		if (mutex == NULL)
187		{
188			// if the object didn't have a mutex, it wasn't cached.
189			// Just clean it up and get out.
190			obj->aboutToDestruct(); // removes the object from its associated cache.
191		}
192		else
193        {
194            StLock<Mutex> _(*mutex);
195
196            if (obj->isNew())
197            {
198                // New objects aren't in the cache.
199                // Just clean it up and get out.
200                obj->aboutToDestruct(); // removes the object from its associated cache.
201                return;
202            }
203
204            obj->aboutToDestruct(); // removes the object from its associated cache.
205        }
206	}
207	catch(...)
208	{
209	}
210
211    if (isCollectable)
212    {
213        delete obj;
214    }
215}
216
217Boolean
218CFClass::equalType(CFTypeRef cf1, CFTypeRef cf2) throw()
219{
220	// CF checks for pointer equality and ensures type equality already
221	try {
222		return SecCFObject::optional(cf1)->equal(*SecCFObject::optional(cf2));
223	} catch (...) {
224		return false;
225	}
226}
227
228CFHashCode
229CFClass::hashType(CFTypeRef cf) throw()
230{
231	try {
232		return SecCFObject::optional(cf)->hash();
233	} catch (...) {
234		return 666; /* Beasty return for error */
235	}
236}
237
238CFStringRef
239CFClass::copyFormattingDescType(CFTypeRef cf, CFDictionaryRef dict) throw()
240{
241	try {
242		return SecCFObject::optional(cf)->copyFormattingDesc(dict);
243	} catch (...) {
244		return CFSTR("Exception thrown trying to format object");
245	}
246}
247
248CFStringRef
249CFClass::copyDebugDescType(CFTypeRef cf) throw()
250{
251	try {
252		return SecCFObject::optional(cf)->copyDebugDesc();
253	} catch (...) {
254		return CFSTR("Exception thrown trying to format object");
255	}
256}
257
258
259