]> git.saurik.com Git - apple/objc4.git/blob - runtime/Accessors.subproj/objc-accessors.m
objc4-493.11.tar.gz
[apple/objc4.git] / runtime / Accessors.subproj / objc-accessors.m
1 /*
2 * Copyright (c) 2006-2008 Apple 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 #import <string.h>
25 #import <stddef.h>
26
27 #import <libkern/OSAtomic.h>
28
29 #import "objc-private.h"
30 #import "objc-auto.h"
31 #import "runtime.h"
32 #import "objc-accessors.h"
33
34 // stub interface declarations to make compiler happy.
35
36 @interface __NSCopyable
37 - (id)copyWithZone:(void *)zone;
38 @end
39
40 @interface __NSMutableCopyable
41 - (id)mutableCopyWithZone:(void *)zone;
42 @end
43
44
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);
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
57 PRIVATE_EXTERN id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
58 return *(id*) ((char*)self + offset);
59 }
60
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;
65
66 // Atomic retain release world
67 spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
68 _spin_lock(slotlock);
69 id value = objc_retain(*slot);
70 _spin_unlock(slotlock);
71
72 // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
73 return objc_autoreleaseReturnValue(value);
74 }
75
76 id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
77 return
78 #if SUPPORT_GC
79 (UseGC ? objc_getProperty_gc : objc_getProperty_non_gc)
80 #else
81 objc_getProperty_non_gc
82 #endif
83 (self, _cmd, offset, atomic);
84 }
85
86 enum { OBJC_PROPERTY_RETAIN = 0, OBJC_PROPERTY_COPY = 1, OBJC_PROPERTY_MUTABLECOPY = 2 };
87
88 #if SUPPORT_GC
89 PRIVATE_EXTERN void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
90 if (shouldCopy) {
91 newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
92 }
93 objc_assign_ivar_gc(newValue, self, offset);
94 }
95 #endif
96
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);
100
101 // atomic or not, if slot would be unchanged, do nothing.
102 if (!shouldCopy && *slot == newValue) return;
103
104 if (shouldCopy) {
105 newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
106 } else {
107 newValue = objc_retain(newValue);
108 }
109
110 if (!atomic) {
111 oldValue = *slot;
112 *slot = newValue;
113 } else {
114 spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
115 _spin_lock(slotlock);
116 oldValue = *slot;
117 *slot = newValue;
118 _spin_unlock(slotlock);
119 }
120
121 objc_release(oldValue);
122 }
123
124 void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
125 #if SUPPORT_GC
126 (UseGC ? objc_setProperty_gc : objc_setProperty_non_gc)
127 #else
128 objc_setProperty_non_gc
129 #endif
130 (self, _cmd, offset, newValue, atomic, shouldCopy);
131 }
132
133
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;
141 if (atomic) {
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)];
148 }
149 else if (lockfirst == locksecond) {
150 // lucky - we only need one lock
151 locksecond = NULL;
152 }
153 _spin_lock(lockfirst);
154 if (locksecond) _spin_lock(locksecond);
155 }
156 #if SUPPORT_GC
157 if (UseGC && hasStrong) {
158 auto_zone_write_barrier_memmove(gc_zone, dest, src, size);
159 } else
160 #endif
161 {
162 memmove(dest, src, size);
163 }
164 if (atomic) {
165 _spin_unlock(lockfirst);
166 if (locksecond) _spin_unlock(locksecond);
167 }
168 }
169