/* Insert a class in the table (used when a new class is registered). */ static void class_table_insert (const char *class_name, Class class_pointer) { int hash, length; class_node_ptr new_node; /* Find out the class name's hash and length. */ CLASS_TABLE_HASH (length, hash, class_name); /* Prepare the new node holding the class. */ new_node = objc_malloc (sizeof (struct class_node)); new_node->name = class_name; new_node->length = length; new_node->pointer = class_pointer; /* Lock the table for modifications. */ objc_mutex_lock (__class_table_lock); /* Insert the new node in the table at the beginning of the table at class_table_array[hash]. */ new_node->next = class_table_array[hash]; class_table_array[hash] = new_node; objc_mutex_unlock (__class_table_lock); }
/* Detach a new thread of execution and return its id. Returns NULL if fails. Thread is started by sending message with selector to object. Message takes a single argument. */ objc_thread_t objc_thread_detach(SEL selector, id object, id argument) { struct __objc_thread_start_state *istate; objc_thread_t thread_id = NULL; /* Allocate the state structure */ if (!(istate = (struct __objc_thread_start_state *) objc_malloc(sizeof(*istate)))) return NULL; /* Initialize the state structure */ istate->selector = selector; istate->object = object; istate->argument = argument; /* lock access */ objc_mutex_lock(__objc_runtime_mutex); /* Call the backend to spawn the thread */ if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function, istate)) == NULL) { /* failed! */ objc_mutex_unlock(__objc_runtime_mutex); objc_free(istate); return NULL; } /* Increment our thread counter */ __objc_runtime_threads_alive++; objc_mutex_unlock(__objc_runtime_mutex); return thread_id; }
void method_exchangeImplementations (struct objc_method * method_a, struct objc_method * method_b) { IMP old_implementation_a; IMP old_implementation_b; if (method_a == NULL || method_b == NULL) return; /* We lock the runtime mutex so that concurrent calls to exchange similar methods won't conflict with each other. Each of them should be atomic. */ objc_mutex_lock (__objc_runtime_mutex); old_implementation_a = method_a->method_imp; old_implementation_b = method_b->method_imp; method_a->method_imp = old_implementation_b; method_b->method_imp = old_implementation_a; /* That was easy :-). But now we need to find all classes that use these methods, and update the IMP in the dispatch tables. */ __objc_update_classes_with_methods (method_a, method_b); objc_mutex_unlock (__objc_runtime_mutex); }
/* Replace a class in the table (used only by poseAs:). */ static void class_table_replace (Class old_class_pointer, Class new_class_pointer) { int hash; class_node_ptr node; objc_mutex_lock (__class_table_lock); hash = 0; node = class_table_array[hash]; while (hash < CLASS_TABLE_SIZE) { if (node == NULL) { hash++; if (hash < CLASS_TABLE_SIZE) { node = class_table_array[hash]; } } else { Class class1 = node->pointer; if (class1 == old_class_pointer) { node->pointer = new_class_pointer; } node = node->next; } } objc_mutex_unlock (__class_table_lock); }
BOOL class_addProtocol (Class class_, Protocol *protocol) { struct objc_protocol_list *protocols; if (class_ == Nil || protocol == NULL) return NO; if (class_conformsToProtocol (class_, protocol)) return NO; /* Check that it is a Protocol object before casting it to (struct objc_protocol *). */ if (protocol->class_pointer != objc_lookUpClass ("Protocol")) return NO; objc_mutex_lock (__objc_runtime_mutex); /* Create the objc_protocol_list. */ protocols = malloc (sizeof (struct objc_protocol_list)); protocols->count = 1; protocols->list[0] = (struct objc_protocol *)protocol; /* Attach it to the list of class protocols. */ protocols->next = class_->protocols; class_->protocols = protocols; objc_mutex_unlock (__objc_runtime_mutex); return YES; }
/* Make the objc thread system aware that a thread managed (started, stopped) by some external code will no longer access objc and thus can be forgotten by the objc thread system. Call objc_thread_remove() when your alien thread is done with making calls to Objective-C. */ void objc_thread_remove(void) { objc_mutex_lock(__objc_runtime_mutex); __objc_runtime_threads_alive--; objc_mutex_unlock(__objc_runtime_mutex); }
/* Make the objc thread system aware that a thread which is managed (started, stopped) by external code could access objc facilities from now on. This is used when you are interfacing with some external non-objc-based environment/system - you must call objc_thread_add() before an alien thread makes any calls to Objective-C. Do not cause the _objc_became_multi_threaded hook to be executed. */ void objc_thread_add(void) { objc_mutex_lock(__objc_runtime_mutex); __objc_is_multi_threaded = 1; __objc_runtime_threads_alive++; objc_mutex_unlock(__objc_runtime_mutex); }
/* Terminate the current tread. Doesn't return. Actually, if it failed returns -1. */ int objc_thread_exit(void) { /* Decrement our counter of the number of threads alive */ objc_mutex_lock(__objc_runtime_mutex); __objc_runtime_threads_alive--; objc_mutex_unlock(__objc_runtime_mutex); /* Call the backend to terminate the thread */ return __objc_thread_exit(); }
void __objc_init_class_tables() { /* Allocate the class hash table. */ if(__class_table_lock) return; objc_mutex_lock(__objc_runtime_mutex); class_table_setup (); objc_mutex_unlock(__objc_runtime_mutex); }
Protocol * objc_getProtocol (const char *name) { Protocol *protocol; if (name == NULL) return NULL; objc_mutex_lock (__protocols_hashtable_lock); protocol = (Protocol *)(objc_hash_value_for_key (__protocols_hashtable, name)); objc_mutex_unlock (__protocols_hashtable_lock); return protocol; }
void __objc_init_class_tables() { /* Allocate the class hash table */ if(__objc_class_hash) return; objc_mutex_lock(__objc_runtime_mutex); __objc_class_hash = hash_new (CLASS_HASH_SIZE, (hash_func_type) hash_string, (compare_func_type) compare_strings); objc_mutex_unlock(__objc_runtime_mutex); }
static void sarray_free_garbage (void *vp) { objc_mutex_lock (__objc_runtime_mutex); if (__objc_runtime_threads_alive == 1) { objc_free (vp); if (first_free_data) sarray_remove_garbage (); } else { *(void **)vp = first_free_data; first_free_data = vp; } objc_mutex_unlock (__objc_runtime_mutex); }
/* This function removes any structures left over from free operations that were not safe in a multi-threaded environment. */ void sarray_remove_garbage (void) { void **vp; void *np; objc_mutex_lock (__objc_runtime_mutex); vp = first_free_data; first_free_data = NULL; while (vp) { np = *vp; objc_free (vp); vp = np; } objc_mutex_unlock (__objc_runtime_mutex); }
/* Add a protocol to the hashtable. */ void __objc_protocols_add_protocol (const char *name, struct objc_protocol *object) { objc_mutex_lock (__protocols_hashtable_lock); /* If we find a protocol with the same name already in the hashtable, we do not need to add the new one, because it will be identical to it. This in the reasonable assumption that two protocols with the same name are identical, which is expected in any sane program. If we are really paranoid, we would compare the protocols and abort if they are not identical. Unfortunately, this would slow down the startup of all Objective-C programs while trying to catch a problem that has never been seen in practice, so we don't do it. */ if (! objc_hash_is_key_in_hash (__protocols_hashtable, name)) objc_hash_add (&__protocols_hashtable, name, object); objc_mutex_unlock (__protocols_hashtable_lock); }
BOOL class_conformsToProtocol (Class class_, Protocol *protocol) { struct objc_protocol_list* proto_list; if (class_ == Nil || protocol == NULL) return NO; /* Check that it is a Protocol object before casting it to (struct objc_protocol *). */ if (protocol->class_pointer != objc_lookUpClass ("Protocol")) return NO; /* Acquire the runtime lock because the list of protocols for a class may be modified concurrently, for example if another thread calls class_addProtocol(), or dynamically loads from a file a category of the class. */ objc_mutex_lock (__objc_runtime_mutex); proto_list = class_->protocols; while (proto_list) { size_t i; for (i = 0; i < proto_list->count; i++) { if (proto_list->list[i] == (struct objc_protocol *)protocol || protocol_conformsToProtocol ((Protocol *)proto_list->list[i], protocol)) { objc_mutex_unlock (__objc_runtime_mutex); return YES; } } proto_list = proto_list->next; } objc_mutex_unlock (__objc_runtime_mutex); return NO; }
/* Deallocate a mutex. Note that this includes an implicit mutex_lock to insure that no one else is using the lock. It is legal to deallocate a lock if we have a lock on it, but illegal to deallocate a lock held by anyone else. Returns the number of locks on the thread. (1 for deallocate). */ int objc_mutex_deallocate(objc_mutex_t mutex) { int depth; /* Valid mutex? */ if (!mutex) return -1; /* Acquire lock on mutex */ depth = objc_mutex_lock(mutex); /* Call backend to destroy mutex */ if (__objc_mutex_deallocate(mutex)) return -1; /* Free the mutex structure */ objc_free(mutex); /* Return last depth */ return depth; }
IMP method_setImplementation (struct objc_method * method, IMP implementation) { IMP old_implementation; if (method == NULL || implementation == NULL) return NULL; /* We lock the runtime mutex so that concurrent calls to change the same method won't conflict with each other. */ objc_mutex_lock (__objc_runtime_mutex); old_implementation = method->method_imp; method->method_imp = implementation; /* That was easy :-). But now we need to find all classes that use this method, and update the IMP in the dispatch tables. */ __objc_update_classes_with_methods (method, NULL); objc_mutex_unlock (__objc_runtime_mutex); return old_implementation; }
int objc_sync_enter (id object) { #ifndef SYNC_CACHE_DISABLE int free_cache_slot; #endif int hash; lock_node_ptr node; lock_node_ptr unused_node; if (object == nil) return OBJC_SYNC_SUCCESS; #ifndef SYNC_CACHE_DISABLE if (lock_cache == NULL) { /* Note that this calloc only happen only once per thread, the very first time a thread does a objc_sync_enter(). */ lock_cache = objc_calloc (SYNC_CACHE_SIZE, sizeof (lock_node_ptr)); } /* Check the cache to see if we have a record of having already locked the lock corresponding to this object. While doing so, keep track of the first free cache node in case we need it later. */ node = NULL; free_cache_slot = -1; { int i; for (i = 0; i < SYNC_CACHE_SIZE; i++) { lock_node_ptr locked_node = lock_cache[i]; if (locked_node == NULL) { if (free_cache_slot == -1) free_cache_slot = i; } else if (locked_node->object == object) { node = locked_node; break; } } } if (node != NULL) { /* We found the lock. Increase recursive_usage_count, which is protected by node->lock, which we already hold. */ node->recursive_usage_count++; /* There is no need to actually lock anything, since we already hold the lock. Correspondingly, objc_sync_exit() will just decrease recursive_usage_count and do nothing to unlock. */ return OBJC_SYNC_SUCCESS; } #endif /* SYNC_CACHE_DISABLE */ /* The following is the standard lookup for the lock in the standard pool lock. It requires a pool protection lock. */ hash = SYNC_OBJECT_HASH(object); /* Search for an existing lock for 'object'. While searching, make note of any unused lock if we find any. */ unused_node = NULL; objc_mutex_lock (sync_pool_protection_locks[hash]); node = sync_pool_array[hash]; while (node != NULL) { if (node->object == object) { /* We found the lock. */ node->usage_count++; objc_mutex_unlock (sync_pool_protection_locks[hash]); #ifndef SYNC_CACHE_DISABLE /* Put it in the cache. */ if (free_cache_slot != -1) lock_cache[free_cache_slot] = node; #endif /* Lock it. */ objc_mutex_lock (node->lock); return OBJC_SYNC_SUCCESS; } if (unused_node == NULL && node->usage_count == 0) { /* We found the first unused node. Record it. */ unused_node = node; } node = node->next; } /* An existing lock for 'object' could not be found. */ if (unused_node != NULL) { /* But we found a unused lock; use it. */ unused_node->object = object; unused_node->usage_count = 1; unused_node->recursive_usage_count = 0; objc_mutex_unlock (sync_pool_protection_locks[hash]); #ifndef SYNC_CACHE_DISABLE if (free_cache_slot != -1) lock_cache[free_cache_slot] = unused_node; #endif objc_mutex_lock (unused_node->lock); return OBJC_SYNC_SUCCESS; } else { /* There are no unused nodes; allocate a new node. */ lock_node_ptr new_node; /* Create the node. */ new_node = objc_malloc (sizeof (struct lock_node)); new_node->lock = objc_mutex_allocate (); new_node->object = object; new_node->usage_count = 1; new_node->recursive_usage_count = 0; /* Attach it at the beginning of the pool. */ new_node->next = sync_pool_array[hash]; sync_pool_array[hash] = new_node; objc_mutex_unlock (sync_pool_protection_locks[hash]); #ifndef SYNC_CACHE_DISABLE if (free_cache_slot != -1) lock_cache[free_cache_slot] = new_node; #endif objc_mutex_lock (new_node->lock); return OBJC_SYNC_SUCCESS; } }
int objc_sync_exit (id object) { int hash; lock_node_ptr node; if (object == nil) return OBJC_SYNC_SUCCESS; #ifndef SYNC_CACHE_DISABLE if (lock_cache != NULL) { int i; /* Find the lock in the cache. */ node = NULL; for (i = 0; i < SYNC_CACHE_SIZE; i++) { lock_node_ptr locked_node = lock_cache[i]; if (locked_node != NULL && locked_node->object == object) { node = locked_node; break; } } /* Note that, if a node was found in the cache, the variable i now holds the index where it was found, which will be used to remove it from the cache. */ if (node != NULL) { if (node->recursive_usage_count > 0) { node->recursive_usage_count--; return OBJC_SYNC_SUCCESS; } else { /* We need to do a real unlock. */ hash = SYNC_OBJECT_HASH(object); /* TODO: If we had atomic increase/decrease operations with memory barriers, we could avoid the lock here! */ objc_mutex_lock (sync_pool_protection_locks[hash]); node->usage_count--; /* Normally, we do not reset object to nil here. We'll leave the lock associated with that object, at zero usage count. This makes it slightly more efficient to provide a lock for that object if (as likely) requested again. If the object is deallocated, we don't care. It will never match a new lock that is requested, and the node will be reused at some point. But, if garbage collection is enabled, leaving a pointer to the object in memory might prevent the object from being released. In that case, we remove it (TODO: maybe we should avoid using the garbage collector at all ? Nothing is ever deallocated in this file). */ #if OBJC_WITH_GC node->object = nil; #endif objc_mutex_unlock (sync_pool_protection_locks[hash]); /* PS: Between objc_mutex_unlock (sync_pool_protection_locks[hash]) and objc_mutex_unlock (node->lock), the pool is unlocked so other threads may allocate this same lock to another object (!). This is not a problem, but it is curious. */ objc_mutex_unlock (node->lock); /* Remove the node from the cache. */ lock_cache[i] = NULL; return OBJC_SYNC_SUCCESS; } } } #endif /* The cache either wasn't there, or didn't work (eg, we overflowed it at some point and stopped recording new locks in the cache). Proceed with a full search of the lock pool. */ hash = SYNC_OBJECT_HASH(object); objc_mutex_lock (sync_pool_protection_locks[hash]); /* Search for an existing lock for 'object'. */ node = sync_pool_array[hash]; while (node != NULL) { if (node->object == object) { /* We found the lock. */ node->usage_count--; objc_mutex_unlock (sync_pool_protection_locks[hash]); objc_mutex_unlock (node->lock); /* No need to remove the node from the cache, since it wasn't found in the cache when we looked for it! */ return OBJC_SYNC_SUCCESS; } node = node->next; } objc_mutex_unlock (sync_pool_protection_locks[hash]); /* A lock for 'object' to unlock could not be found (!!). */ return OBJC_SYNC_NOT_OWNING_THREAD_ERROR; }