]>
Commit | Line | Data |
---|---|---|
b3962a83 | 1 | /* |
7af964d1 | 2 | * Copyright (c) 2006-2008 Apple Inc. All Rights Reserved. |
b3962a83 A |
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 | ||
cd5f04f5 A |
24 | #include <string.h> |
25 | #include <stddef.h> | |
b3962a83 | 26 | |
cd5f04f5 | 27 | #include <libkern/OSAtomic.h> |
b3962a83 | 28 | |
cd5f04f5 A |
29 | #include "objc-private.h" |
30 | #include "objc-auto.h" | |
31 | #include "runtime.h" | |
32 | #include "objc-accessors.h" | |
b3962a83 A |
33 | |
34 | // stub interface declarations to make compiler happy. | |
35 | ||
36 | @interface __NSCopyable | |
37 | - (id)copyWithZone:(void *)zone; | |
38 | @end | |
39 | ||
7af964d1 A |
40 | @interface __NSMutableCopyable |
41 | - (id)mutableCopyWithZone:(void *)zone; | |
42 | @end | |
43 | ||
b3962a83 A |
44 | |
45 | typedef uintptr_t spin_lock_t; | |
cd5f04f5 A |
46 | OBJC_EXTERN void _spin_lock(spin_lock_t *lockp); |
47 | OBJC_EXTERN int _spin_lock_try(spin_lock_t *lockp); | |
48 | OBJC_EXTERN void _spin_unlock(spin_lock_t *lockp); | |
b3962a83 A |
49 | |
50 | /* need to consider cache line contention - space locks out XXX */ | |
51 | ||
52 | #define GOODPOWER 7 | |
53 | #define GOODMASK ((1<<GOODPOWER)-1) | |
54 | #define GOODHASH(x) (((long)x >> 5) & GOODMASK) | |
55 | static spin_lock_t PropertyLocks[1 << GOODPOWER] = { 0 }; | |
56 | ||
cd5f04f5 A |
57 | #define MUTABLE_COPY 2 |
58 | ||
cd5f04f5 | 59 | id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { |
8070259c A |
60 | if (offset == 0) { |
61 | return object_getClass(self); | |
62 | } | |
63 | ||
b3962a83 A |
64 | // Retain release world |
65 | id *slot = (id*) ((char*)self + offset); | |
66 | if (!atomic) return *slot; | |
67 | ||
68 | // Atomic retain release world | |
69 | spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)]; | |
70 | _spin_lock(slotlock); | |
8972963c | 71 | id value = objc_retain(*slot); |
b3962a83 A |
72 | _spin_unlock(slotlock); |
73 | ||
74 | // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock. | |
8972963c A |
75 | return objc_autoreleaseReturnValue(value); |
76 | } | |
77 | ||
b3962a83 | 78 | |
cd5f04f5 | 79 | static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) __attribute__((always_inline)); |
b3962a83 | 80 | |
cd5f04f5 A |
81 | static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) |
82 | { | |
8070259c A |
83 | if (offset == 0) { |
84 | object_setClass(self, newValue); | |
85 | return; | |
86 | } | |
87 | ||
cd5f04f5 A |
88 | id oldValue; |
89 | id *slot = (id*) ((char*)self + offset); | |
90 | ||
91 | if (copy) { | |
92 | newValue = [newValue copyWithZone:NULL]; | |
93 | } else if (mutableCopy) { | |
94 | newValue = [newValue mutableCopyWithZone:NULL]; | |
7af964d1 | 95 | } else { |
cd5f04f5 | 96 | if (*slot == newValue) return; |
8972963c | 97 | newValue = objc_retain(newValue); |
7af964d1 | 98 | } |
b3962a83 A |
99 | |
100 | if (!atomic) { | |
101 | oldValue = *slot; | |
102 | *slot = newValue; | |
103 | } else { | |
104 | spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)]; | |
105 | _spin_lock(slotlock); | |
106 | oldValue = *slot; | |
107 | *slot = newValue; | |
cd5f04f5 | 108 | _spin_unlock(slotlock); |
b3962a83 A |
109 | } |
110 | ||
8972963c A |
111 | objc_release(oldValue); |
112 | } | |
113 | ||
cd5f04f5 A |
114 | void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) |
115 | { | |
116 | bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY); | |
117 | bool mutableCopy = (shouldCopy == MUTABLE_COPY); | |
118 | reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); | |
119 | } | |
120 | ||
121 | void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) | |
122 | { | |
123 | reallySetProperty(self, _cmd, newValue, offset, true, false, false); | |
124 | } | |
125 | ||
126 | void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) | |
127 | { | |
128 | reallySetProperty(self, _cmd, newValue, offset, false, false, false); | |
129 | } | |
130 | ||
131 | ||
132 | void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) | |
133 | { | |
134 | reallySetProperty(self, _cmd, newValue, offset, true, true, false); | |
135 | } | |
136 | ||
137 | void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) | |
138 | { | |
139 | reallySetProperty(self, _cmd, newValue, offset, false, true, false); | |
140 | } | |
141 | ||
142 | ||
8972963c | 143 | #if SUPPORT_GC |
7257e56c A |
144 | |
145 | id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { | |
146 | return *(id*) ((char*)self + offset); | |
147 | } | |
148 | ||
149 | void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { | |
150 | if (shouldCopy) { | |
151 | newValue = (shouldCopy == MUTABLE_COPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]); | |
152 | } | |
153 | objc_assign_ivar(newValue, self, offset); | |
154 | } | |
155 | ||
156 | // objc_getProperty and objc_setProperty are resolver functions in objc-auto.mm | |
157 | ||
8972963c | 158 | #else |
7257e56c A |
159 | |
160 | id | |
161 | objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) | |
162 | { | |
163 | return objc_getProperty_non_gc(self, _cmd, offset, atomic); | |
b3962a83 A |
164 | } |
165 | ||
7257e56c A |
166 | void |
167 | objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, | |
168 | BOOL atomic, signed char shouldCopy) | |
169 | { | |
170 | objc_setProperty_non_gc(self, _cmd, offset, newValue, atomic, shouldCopy); | |
171 | } | |
172 | ||
173 | #endif | |
174 | ||
b3962a83 | 175 | |
b3962a83 A |
176 | // This entry point was designed wrong. When used as a getter, src needs to be locked so that |
177 | // if simultaneously used for a setter then there would be contention on src. | |
178 | // So we need two locks - one of which will be contended. | |
179 | void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong) { | |
180 | static spin_lock_t StructLocks[1 << GOODPOWER] = { 0 }; | |
181 | spin_lock_t *lockfirst = NULL; | |
182 | spin_lock_t *locksecond = NULL; | |
183 | if (atomic) { | |
184 | lockfirst = &StructLocks[GOODHASH(src)]; | |
185 | locksecond = &StructLocks[GOODHASH(dest)]; | |
186 | // order the locks by address so that we don't deadlock | |
187 | if (lockfirst > locksecond) { | |
188 | lockfirst = locksecond; | |
189 | locksecond = &StructLocks[GOODHASH(src)]; | |
190 | } | |
191 | else if (lockfirst == locksecond) { | |
192 | // lucky - we only need one lock | |
193 | locksecond = NULL; | |
194 | } | |
195 | _spin_lock(lockfirst); | |
196 | if (locksecond) _spin_lock(locksecond); | |
197 | } | |
8972963c | 198 | #if SUPPORT_GC |
b3962a83 A |
199 | if (UseGC && hasStrong) { |
200 | auto_zone_write_barrier_memmove(gc_zone, dest, src, size); | |
8972963c A |
201 | } else |
202 | #endif | |
203 | { | |
b3962a83 A |
204 | memmove(dest, src, size); |
205 | } | |
206 | if (atomic) { | |
207 | _spin_unlock(lockfirst); | |
208 | if (locksecond) _spin_unlock(locksecond); | |
209 | } | |
210 | } | |
211 | ||
cd5f04f5 A |
212 | void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)) { |
213 | static spin_lock_t CppObjectLocks[1 << GOODPOWER] = { 0 }; | |
214 | spin_lock_t *lockfirst = &CppObjectLocks[GOODHASH(src)], *locksecond = &CppObjectLocks[GOODHASH(dest)]; | |
215 | // order the locks by address so that we don't deadlock | |
216 | if (lockfirst > locksecond) { | |
217 | spin_lock_t *temp = lockfirst; | |
218 | lockfirst = locksecond; | |
219 | locksecond = temp; | |
220 | } else if (lockfirst == locksecond) { | |
221 | // lucky - we only need one lock | |
222 | locksecond = NULL; | |
223 | } | |
224 | _spin_lock(lockfirst); | |
225 | if (locksecond) _spin_lock(locksecond); | |
226 | ||
227 | // let C++ code perform the actual copy. | |
228 | copyHelper(dest, src); | |
229 | ||
230 | _spin_unlock(lockfirst); | |
231 | if (locksecond) _spin_unlock(locksecond); | |
232 | } |