| /* GNU Objective C Runtime accessors functions | |
| Copyright (C) 2010-2025 Free Software Foundation, Inc. | |
| Contributed by Nicola Pero | |
| This file is part of GCC. | |
| GCC is free software; you can redistribute it and/or modify it under the | |
| terms of the GNU General Public License as published by the Free Software | |
| Foundation; either version 3, or (at your option) any later version. | |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
| details. | |
| Under Section 7 of GPL version 3, you are granted additional | |
| permissions described in the GCC Runtime Library Exception, version | |
| 3.1, as published by the Free Software Foundation. | |
| You should have received a copy of the GNU General Public License and | |
| a copy of the GCC Runtime Library Exception along with this program; | |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| <http://www.gnu.org/licenses/>. */ | |
| #include "objc-private/common.h" | |
| #include "objc/objc.h" | |
| #include "objc/thr.h" | |
| #include <string.h> /* For memcpy */ | |
| /* This file contains functions that the compiler uses when | |
| synthesizing accessors (getters/setters) for properties. The | |
| functions are part of the ABI, but are meant to be used by the | |
| compiler and not by users; for this reason, they are not declared | |
| in public header files. The compiler automatically generates | |
| declarations for these functions. */ | |
| /* Properties can be "atomic", which requires protecting them from | |
| concurrency issues using a lock. Unfortunately, we can't have a | |
| lock for each property, so we'll go with a small pool of locks. | |
| Any time a property is accessed in an "atomic" way, we pick a | |
| random lock from the pool (random, but always the same one for the | |
| same property of the same object) and use it to protect access to | |
| the property. | |
| The size of the pool is currently 16. A bigger pool can help | |
| reduce contention, ie, reduce the chances that two threads, | |
| operating on unrelated properties, will have to wait for each other | |
| because the properties use the same lock. 16 seems big enough at | |
| the moment. */ | |
| #define ACCESSORS_NUMBER_OF_LOCKS 16 | |
| #define ACCESSORS_HASH(POINTER) ((((size_t)POINTER >> 8) ^ (size_t)POINTER) & (ACCESSORS_NUMBER_OF_LOCKS - 1)) | |
| static objc_mutex_t accessors_locks[ACCESSORS_NUMBER_OF_LOCKS]; | |
| /* This is called at startup to setup the locks. */ | |
| void | |
| __objc_accessors_init (void) | |
| { | |
| int i; | |
| for (i = 0; i < ACCESSORS_NUMBER_OF_LOCKS; i++) | |
| accessors_locks[i] = objc_mutex_allocate (); | |
| } | |
| /* The property accessors automatically call various methods from the | |
| Foundation library (eg, GNUstep-base). These methods are not | |
| implemented here, but we need to declare them so we can compile the | |
| runtime. The Foundation library will need to provide | |
| implementations of these methods (most likely in the root class, | |
| eg, NSObject) as the accessors only work with objects of classes | |
| that implement these methods. */ | |
| @interface _libobjcNSObject | |
| - (id) copyWithZone: (void *)zone; | |
| - (id) mutableCopyWithZone: (void *)zone; | |
| @end | |
| #define COPY(X) [((_libobjcNSObject *)(X)) copyWithZone: NULL] | |
| #define MUTABLE_COPY(X) [((_libobjcNSObject *)(X)) mutableCopyWithZone: NULL] | |
| #if OBJC_WITH_GC | |
| # define AUTORELEASE(X) (X) | |
| # define RELEASE(X) | |
| # define RETAIN(X) (X) | |
| #else | |
| @interface _libobjcNSObject (RetainReleaseMethods) | |
| - (id) autorelease; | |
| - (oneway void) release; | |
| - (id) retain; | |
| @end | |
| # define AUTORELEASE(X) [((_libobjcNSObject *)(X)) autorelease] | |
| # define RELEASE(X) [((_libobjcNSObject *)(X)) release] | |
| # define RETAIN(X) [((_libobjcNSObject *)(X)) retain] | |
| #endif | |
| /* The compiler uses this function when implementing some synthesized | |
| getters for properties of type 'id'. */ | |
| id | |
| objc_getProperty (id self, SEL __attribute__((unused)) _cmd, ptrdiff_t offset, BOOL is_atomic) | |
| { | |
| if (self != nil) | |
| { | |
| id *pointer_to_ivar = (id *)((char *)self + offset); | |
| if (is_atomic == NO) | |
| { | |
| /* Note that in this case, we do not RETAIN/AUTORELEASE the | |
| returned value. The programmer should do it if it is | |
| needed. Since access is non-atomic, other threads can be | |
| ignored and the caller has full control of what happens | |
| to the object and whether it needs to be RETAINed or not, | |
| so it makes sense to leave the decision to him/her. This | |
| is also what the Apple/NeXT runtime does. */ | |
| return *pointer_to_ivar; | |
| } | |
| else | |
| { | |
| objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (pointer_to_ivar)]; | |
| id result; | |
| objc_mutex_lock (lock); | |
| result = RETAIN (*(pointer_to_ivar)); | |
| objc_mutex_unlock (lock); | |
| return AUTORELEASE (result); | |
| } | |
| } | |
| return nil; | |
| } | |
| /* The compiler uses this function when implementing some synthesized | |
| setters for properties of type 'id'. | |
| PS: Note how 'should_copy' is declared 'BOOL' but then actually | |
| takes values from 0 to 2. This hack was introduced by Apple; we | |
| do the same for compatibility reasons. */ | |
| void | |
| objc_setProperty (id self, SEL __attribute__((unused)) _cmd, ptrdiff_t offset, id new_value, BOOL is_atomic, BOOL should_copy) | |
| { | |
| if (self != nil) | |
| { | |
| id *pointer_to_ivar = (id *)((char *)self + offset); | |
| id retained_value; | |
| #if !OBJC_WITH_GC | |
| id old_value; | |
| #endif | |
| switch (should_copy) | |
| { | |
| case 0: /* retain */ | |
| { | |
| if (*pointer_to_ivar == new_value) | |
| return; | |
| retained_value = RETAIN (new_value); | |
| break; | |
| } | |
| case 2: /* mutable copy */ | |
| { | |
| retained_value = MUTABLE_COPY (new_value); | |
| break; | |
| } | |
| case 1: /* copy */ | |
| default: | |
| { | |
| retained_value = COPY (new_value); | |
| break; | |
| } | |
| } | |
| if (is_atomic == NO) | |
| { | |
| #if !OBJC_WITH_GC | |
| old_value = *pointer_to_ivar; | |
| #endif | |
| *pointer_to_ivar = retained_value; | |
| } | |
| else | |
| { | |
| objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (pointer_to_ivar)]; | |
| objc_mutex_lock (lock); | |
| #if !OBJC_WITH_GC | |
| old_value = *pointer_to_ivar; | |
| #endif | |
| *pointer_to_ivar = retained_value; | |
| objc_mutex_unlock (lock); | |
| } | |
| #if !OBJC_WITH_GC | |
| RELEASE (old_value); | |
| #endif | |
| } | |
| } | |
| /* The compiler uses this function when implementing some synthesized | |
| getters for properties of arbitrary C types. The data is just | |
| copied. Compatibility Note: this function does not exist in the | |
| Apple/NeXT runtime. */ | |
| void | |
| objc_getPropertyStruct (void *destination, const void *source, ptrdiff_t size, BOOL is_atomic, BOOL __attribute__((unused)) has_strong) | |
| { | |
| if (is_atomic == NO) | |
| memcpy (destination, source, size); | |
| else | |
| { | |
| objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (source)]; | |
| objc_mutex_lock (lock); | |
| memcpy (destination, source, size); | |
| objc_mutex_unlock (lock); | |
| } | |
| } | |
| /* The compiler uses this function when implementing some synthesized | |
| setters for properties of arbitrary C types. The data is just | |
| copied. Compatibility Note: this function does not exist in the | |
| Apple/NeXT runtime. */ | |
| void | |
| objc_setPropertyStruct (void *destination, const void *source, ptrdiff_t size, BOOL is_atomic, BOOL __attribute__((unused)) has_strong) | |
| { | |
| if (is_atomic == NO) | |
| memcpy (destination, source, size); | |
| else | |
| { | |
| objc_mutex_t lock = accessors_locks[ACCESSORS_HASH (destination)]; | |
| objc_mutex_lock (lock); | |
| memcpy (destination, source, size); | |
| objc_mutex_unlock (lock); | |
| } | |
| } | |
| /* This is the function that the Apple/NeXT runtime has instead of | |
| objc_getPropertyStruct and objc_setPropertyStruct. We include it | |
| for API compatibility (just for people who may have used | |
| objc_copyStruct on the NeXT runtime thinking it was a public API); | |
| the compiler never generates calls to it with the GNU runtime. | |
| This function is clumsy because it requires two locks instead of | |
| one. */ | |
| void | |
| objc_copyStruct (void *destination, const void *source, ptrdiff_t size, BOOL is_atomic, BOOL __attribute__((unused)) has_strong) | |
| { | |
| if (is_atomic == NO) | |
| memcpy (destination, source, size); | |
| else | |
| { | |
| /* We don't know which one is the property, so we have to lock | |
| both. One of them is most likely a temporary buffer in the | |
| local stack and we really wouldn't want to lock it (our | |
| objc_getPropertyStruct and objc_setPropertyStruct functions | |
| don't lock it). Note that if we're locking more than one | |
| accessor lock at once, we need to always lock them in the | |
| same order to avoid deadlocks. */ | |
| objc_mutex_t first_lock; | |
| objc_mutex_t second_lock; | |
| if (ACCESSORS_HASH (source) == ACCESSORS_HASH (destination)) | |
| { | |
| /* A lucky collision. */ | |
| first_lock = accessors_locks[ACCESSORS_HASH (source)]; | |
| objc_mutex_lock (first_lock); | |
| memcpy (destination, source, size); | |
| objc_mutex_unlock (first_lock); | |
| return; | |
| } | |
| if (ACCESSORS_HASH (source) > ACCESSORS_HASH (destination)) | |
| { | |
| first_lock = accessors_locks[ACCESSORS_HASH (source)]; | |
| second_lock = accessors_locks[ACCESSORS_HASH (destination)]; | |
| } | |
| else | |
| { | |
| first_lock = accessors_locks[ACCESSORS_HASH (destination)]; | |
| second_lock = accessors_locks[ACCESSORS_HASH (source)]; | |
| } | |
| objc_mutex_lock (first_lock); | |
| objc_mutex_lock (second_lock); | |
| memcpy (destination, source, size); | |
| objc_mutex_unlock (second_lock); | |
| objc_mutex_unlock (first_lock); | |
| } | |
| } |