]>
Commit | Line | Data |
---|---|---|
682e805a | 1 | /* GNU Objective C Runtime accessors functions |
a945c346 | 2 | Copyright (C) 2010-2024 Free Software Foundation, Inc. |
682e805a NP |
3 | Contributed by Nicola Pero |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it under the | |
8 | terms of the GNU General Public License as published by the Free Software | |
9 | Foundation; either version 3, or (at your option) any later version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
14 | details. | |
15 | ||
16 | Under Section 7 of GPL version 3, you are granted additional | |
17 | permissions described in the GCC Runtime Library Exception, version | |
18 | 3.1, as published by the Free Software Foundation. | |
19 | ||
20 | You should have received a copy of the GNU General Public License and | |
21 | a copy of the GCC Runtime Library Exception along with this program; | |
22 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 | <http://www.gnu.org/licenses/>. */ | |
24 | ||
25 | #include "objc-private/common.h" | |
26 | #include "objc/objc.h" | |
27 | #include "objc/thr.h" | |
28 | #include <string.h> /* For memcpy */ | |
29 | ||
30 | /* This file contains functions that the compiler uses when | |
31 | synthesizing accessors (getters/setters) for properties. The | |
32 | functions are part of the ABI, but are meant to be used by the | |
33 | compiler and not by users; for this reason, they are not declared | |
34 | in public header files. The compiler automatically generates | |
35 | declarations for these functions. */ | |
36 | ||
37 | /* Properties can be "atomic", which requires protecting them from | |
38 | concurrency issues using a lock. Unfortunately, we can't have a | |
39 | lock for each property, so we'll go with a small pool of locks. | |
40 | Any time a property is accessed in an "atomic" way, we pick a | |
41 | random lock from the pool (random, but always the same one for the | |
42 | same property of the same object) and use it to protect access to | |
43 | the property. | |
44 | ||
45 | The size of the pool is currently 16. A bigger pool can help | |
46 | reduce contention, ie, reduce the chances that two threads, | |
47 | operating on unrelated properties, will have to wait for each other | |
48 | because the properties use the same lock. 16 seems big enough at | |
49 | the moment. */ | |
50 | #define ACCESSORS_NUMBER_OF_LOCKS 16 | |
51 | ||
52 | #define ACCESSORS_HASH(POINTER) ((((size_t)POINTER >> 8) ^ (size_t)POINTER) & (ACCESSORS_NUMBER_OF_LOCKS - 1)) | |
53 | ||
54 | static objc_mutex_t accessors_locks[ACCESSORS_NUMBER_OF_LOCKS]; | |
55 | ||
56 | /* This is called at startup to setup the locks. */ | |
57 | void | |
58 | __objc_accessors_init (void) | |
59 | { | |
60 | int i; | |
61 | ||
62 | for (i = 0; i < ACCESSORS_NUMBER_OF_LOCKS; i++) | |
63 | accessors_locks[i] = objc_mutex_allocate (); | |
64 | } | |
65 | ||
66 | /* The property accessors automatically call various methods from the | |
67 | Foundation library (eg, GNUstep-base). These methods are not | |
68 | implemented here, but we need to declare them so we can compile the | |
69 | runtime. The Foundation library will need to provide | |
70 | implementations of these methods (most likely in the root class, | |
71 | eg, NSObject) as the accessors only work with objects of classes | |
72 | that implement these methods. */ | |
73 | @interface _libobjcNSObject | |
74 | - (id) copyWithZone: (void *)zone; | |
75 | - (id) mutableCopyWithZone: (void *)zone; | |
76 | @end | |
77 | #define COPY(X) [((_libobjcNSObject *)(X)) copyWithZone: NULL] | |
78 | #define MUTABLE_COPY(X) [((_libobjcNSObject *)(X)) mutableCopyWithZone: NULL] | |
79 | ||
80 | ||
81 | #if OBJC_WITH_GC | |
82 | ||
83 | # define AUTORELEASE(X) (X) | |
84 | # define RELEASE(X) | |
85 | # define RETAIN(X) (X) | |
86 | ||
87 | #else | |
88 | ||
89 | @interface _libobjcNSObject (RetainReleaseMethods) | |
90 | - (id) autorelease; | |
91 | - (oneway void) release; | |
92 | - (id) retain; | |
93 | @end | |
94 | # define AUTORELEASE(X) [((_libobjcNSObject *)(X)) autorelease] | |
95 | # define RELEASE(X) [((_libobjcNSObject *)(X)) release] | |
96 | # define RETAIN(X) [((_libobjcNSObject *)(X)) retain] | |
97 | ||
98 | #endif | |
99 | ||
100 | /* The compiler uses this function when implementing some synthesized | |
101 | getters for properties of type 'id'. */ | |
102 | id | |
103 | objc_getProperty (id self, SEL __attribute__((unused)) _cmd, ptrdiff_t offset, BOOL is_atomic) | |
104 | { | |
105 | if (self != nil) | |
106 | { | |
107 | id *pointer_to_ivar = (id *)((char *)self + offset); | |
108 | ||
ef7ed5df | 109 | |
682e805a | 110 | if (is_atomic == NO) |
ef7ed5df NP |
111 | { |
112 | /* Note that in this case, we do not RETAIN/AUTORELEASE the | |
113 | returned value. The programmer should do it if it is | |
114 | needed. Since access is non-atomic, other threads can be | |
115 | ignored and the caller has full control of what happens | |
116 | to the object and whether it needs to be RETAINed or not, | |
117 | so it makes sense to leave the decision to him/her. This | |
118 | is also what the Apple/NeXT runtime does. */ | |
119 | return *pointer_to_ivar; | |
120 | } | |
682e805a NP |
121 | else |
122 | { | |
123 | objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (pointer_to_ivar)]; | |
124 | id result; | |
125 | ||
126 | objc_mutex_lock (lock); | |
127 | result = RETAIN (*(pointer_to_ivar)); | |
128 | objc_mutex_unlock (lock); | |
129 | ||
130 | return AUTORELEASE (result); | |
131 | } | |
132 | } | |
133 | ||
134 | return nil; | |
135 | } | |
136 | ||
137 | /* The compiler uses this function when implementing some synthesized | |
138 | setters for properties of type 'id'. | |
139 | ||
140 | PS: Note how 'should_copy' is declared 'BOOL' but then actually | |
141 | takes values from 0 to 2. This hack was introduced by Apple; we | |
142 | do the same for compatibility reasons. */ | |
143 | void | |
144 | objc_setProperty (id self, SEL __attribute__((unused)) _cmd, ptrdiff_t offset, id new_value, BOOL is_atomic, BOOL should_copy) | |
145 | { | |
146 | if (self != nil) | |
147 | { | |
148 | id *pointer_to_ivar = (id *)((char *)self + offset); | |
149 | id retained_value; | |
150 | #if !OBJC_WITH_GC | |
151 | id old_value; | |
152 | #endif | |
153 | ||
154 | switch (should_copy) | |
155 | { | |
156 | case 0: /* retain */ | |
157 | { | |
158 | if (*pointer_to_ivar == new_value) | |
159 | return; | |
160 | retained_value = RETAIN (new_value); | |
161 | break; | |
162 | } | |
163 | case 2: /* mutable copy */ | |
164 | { | |
165 | retained_value = MUTABLE_COPY (new_value); | |
166 | break; | |
167 | } | |
168 | case 1: /* copy */ | |
169 | default: | |
170 | { | |
171 | retained_value = COPY (new_value); | |
172 | break; | |
173 | } | |
174 | } | |
175 | ||
176 | if (is_atomic == NO) | |
177 | { | |
178 | #if !OBJC_WITH_GC | |
179 | old_value = *pointer_to_ivar; | |
180 | #endif | |
181 | *pointer_to_ivar = retained_value; | |
182 | } | |
183 | else | |
184 | { | |
185 | objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (pointer_to_ivar)]; | |
186 | ||
187 | objc_mutex_lock (lock); | |
188 | #if !OBJC_WITH_GC | |
189 | old_value = *pointer_to_ivar; | |
190 | #endif | |
191 | *pointer_to_ivar = retained_value; | |
192 | objc_mutex_unlock (lock); | |
193 | } | |
194 | #if !OBJC_WITH_GC | |
195 | RELEASE (old_value); | |
196 | #endif | |
197 | } | |
198 | } | |
199 | ||
200 | /* The compiler uses this function when implementing some synthesized | |
201 | getters for properties of arbitrary C types. The data is just | |
202 | copied. Compatibility Note: this function does not exist in the | |
203 | Apple/NeXT runtime. */ | |
204 | void | |
205 | objc_getPropertyStruct (void *destination, const void *source, ptrdiff_t size, BOOL is_atomic, BOOL __attribute__((unused)) has_strong) | |
206 | { | |
207 | if (is_atomic == NO) | |
208 | memcpy (destination, source, size); | |
209 | else | |
210 | { | |
211 | objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (source)]; | |
212 | ||
213 | objc_mutex_lock (lock); | |
214 | memcpy (destination, source, size); | |
215 | objc_mutex_unlock (lock); | |
216 | } | |
217 | } | |
218 | ||
219 | /* The compiler uses this function when implementing some synthesized | |
220 | setters for properties of arbitrary C types. The data is just | |
221 | copied. Compatibility Note: this function does not exist in the | |
222 | Apple/NeXT runtime. */ | |
223 | void | |
224 | objc_setPropertyStruct (void *destination, const void *source, ptrdiff_t size, BOOL is_atomic, BOOL __attribute__((unused)) has_strong) | |
225 | { | |
226 | if (is_atomic == NO) | |
227 | memcpy (destination, source, size); | |
228 | else | |
229 | { | |
230 | objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (destination)]; | |
231 | ||
232 | objc_mutex_lock (lock); | |
233 | memcpy (destination, source, size); | |
234 | objc_mutex_unlock (lock); | |
235 | } | |
236 | } | |
237 | ||
238 | /* This is the function that the Apple/NeXT runtime has instead of | |
239 | objc_getPropertyStruct and objc_setPropertyStruct. We include it | |
240 | for API compatibility (just for people who may have used | |
241 | objc_copyStruct on the NeXT runtime thinking it was a public API); | |
242 | the compiler never generates calls to it with the GNU runtime. | |
243 | This function is clumsy because it requires two locks instead of | |
244 | one. */ | |
245 | void | |
246 | objc_copyStruct (void *destination, const void *source, ptrdiff_t size, BOOL is_atomic, BOOL __attribute__((unused)) has_strong) | |
247 | { | |
248 | if (is_atomic == NO) | |
249 | memcpy (destination, source, size); | |
250 | else | |
251 | { | |
252 | /* We don't know which one is the property, so we have to lock | |
253 | both. One of them is most likely a temporary buffer in the | |
254 | local stack and we really wouldn't want to lock it (our | |
255 | objc_getPropertyStruct and objc_setPropertyStruct functions | |
256 | don't lock it). Note that if we're locking more than one | |
257 | accessor lock at once, we need to always lock them in the | |
258 | same order to avoid deadlocks. */ | |
259 | objc_mutex_t first_lock; | |
260 | objc_mutex_t second_lock; | |
261 | ||
262 | if (ACCESSORS_HASH (source) == ACCESSORS_HASH (destination)) | |
263 | { | |
264 | /* A lucky collision. */ | |
265 | first_lock = accessors_locks[ACCESSORS_HASH (source)]; | |
266 | objc_mutex_lock (first_lock); | |
267 | memcpy (destination, source, size); | |
268 | objc_mutex_unlock (first_lock); | |
269 | return; | |
270 | } | |
271 | ||
272 | if (ACCESSORS_HASH (source) > ACCESSORS_HASH (destination)) | |
273 | { | |
274 | first_lock = accessors_locks[ACCESSORS_HASH (source)]; | |
275 | second_lock = accessors_locks[ACCESSORS_HASH (destination)]; | |
276 | } | |
277 | else | |
278 | { | |
279 | first_lock = accessors_locks[ACCESSORS_HASH (destination)]; | |
280 | second_lock = accessors_locks[ACCESSORS_HASH (source)]; | |
281 | } | |
282 | ||
283 | objc_mutex_lock (first_lock); | |
284 | objc_mutex_lock (second_lock); | |
285 | memcpy (destination, source, size); | |
286 | objc_mutex_unlock (second_lock); | |
287 | objc_mutex_unlock (first_lock); | |
288 | } | |
289 | } |