OSDN Git Service

PR ada/60703
[pf3gnuchains/gcc-fork.git] / libobjc / accessors.m
1 /* GNU Objective C Runtime accessors functions
2    Copyright (C) 2010 Free Software Foundation, Inc.
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
109
110       if (is_atomic == NO)
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         }
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 }