2 * Copyright (c) 2006-2008 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
27 #import <libkern/OSAtomic.h>
29 #import "objc-private.h"
32 #import "objc-accessors.h"
34 // stub interface declarations to make compiler happy.
36 @interface __NSCopyable
37 - (id)copyWithZone:(void *)zone;
40 @interface __NSMutableCopyable
41 - (id)mutableCopyWithZone:(void *)zone;
45 typedef uintptr_t spin_lock_t;
46 extern void _spin_lock(spin_lock_t *lockp);
47 extern int _spin_lock_try(spin_lock_t *lockp);
48 extern void _spin_unlock(spin_lock_t *lockp);
50 /* need to consider cache line contention - space locks out XXX */
53 #define GOODMASK ((1<<GOODPOWER)-1)
54 #define GOODHASH(x) (((long)x >> 5) & GOODMASK)
55 static spin_lock_t PropertyLocks[1 << GOODPOWER] = { 0 };
57 PRIVATE_EXTERN id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
58 return *(id*) ((char*)self + offset);
61 PRIVATE_EXTERN id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
62 // Retain release world
63 id *slot = (id*) ((char*)self + offset);
64 if (!atomic) return *slot;
66 // Atomic retain release world
67 spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
69 id value = objc_retain(*slot);
70 _spin_unlock(slotlock);
72 // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
73 return objc_autoreleaseReturnValue(value);
76 id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
79 (UseGC ? objc_getProperty_gc : objc_getProperty_non_gc)
81 objc_getProperty_non_gc
83 (self, _cmd, offset, atomic);
86 enum { OBJC_PROPERTY_RETAIN = 0, OBJC_PROPERTY_COPY = 1, OBJC_PROPERTY_MUTABLECOPY = 2 };
89 PRIVATE_EXTERN void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
91 newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
93 objc_assign_ivar_gc(newValue, self, offset);
97 PRIVATE_EXTERN void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
98 // Retain release world
99 id oldValue, *slot = (id*) ((char*)self + offset);
101 // atomic or not, if slot would be unchanged, do nothing.
102 if (!shouldCopy && *slot == newValue) return;
105 newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
107 newValue = objc_retain(newValue);
114 spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
115 _spin_lock(slotlock);
118 _spin_unlock(slotlock);
121 objc_release(oldValue);
124 void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
126 (UseGC ? objc_setProperty_gc : objc_setProperty_non_gc)
128 objc_setProperty_non_gc
130 (self, _cmd, offset, newValue, atomic, shouldCopy);
134 // This entry point was designed wrong. When used as a getter, src needs to be locked so that
135 // if simultaneously used for a setter then there would be contention on src.
136 // So we need two locks - one of which will be contended.
137 void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong) {
138 static spin_lock_t StructLocks[1 << GOODPOWER] = { 0 };
139 spin_lock_t *lockfirst = NULL;
140 spin_lock_t *locksecond = NULL;
142 lockfirst = &StructLocks[GOODHASH(src)];
143 locksecond = &StructLocks[GOODHASH(dest)];
144 // order the locks by address so that we don't deadlock
145 if (lockfirst > locksecond) {
146 lockfirst = locksecond;
147 locksecond = &StructLocks[GOODHASH(src)];
149 else if (lockfirst == locksecond) {
150 // lucky - we only need one lock
153 _spin_lock(lockfirst);
154 if (locksecond) _spin_lock(locksecond);
157 if (UseGC && hasStrong) {
158 auto_zone_write_barrier_memmove(gc_zone, dest, src, size);
162 memmove(dest, src, size);
165 _spin_unlock(lockfirst);
166 if (locksecond) _spin_unlock(locksecond);