diff -rc2P objc/Makefile objc-threaded/Makefile *** objc/Makefile Sat Jan 20 21:09:37 1996 --- objc-threaded/Makefile Fri Jan 26 14:39:17 1996 *************** *** 57,61 **** OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o encoding.o \ ! selector.o objects.o misc.o NXConstStr.o Object.o Protocol.o libobjc.a: $(OBJC_O) --- 57,61 ---- OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o encoding.o \ ! selector.o objects.o misc.o NXConstStr.o Object.o Protocol.o thread.o libobjc.a: $(OBJC_O) *************** *** 64,70 **** # ranlib is run in the parent directory's makefile. ! OBJC_H = hash.h list.h sarray.h objc.h \ objc-api.h \ ! NXConstStr.h Object.h Protocol.h encoding.h typedstream.h # copy objc headers to installation include directory --- 64,70 ---- # ranlib is run in the parent directory's makefile. ! OBJC_H = hash.h objc-list.h sarray.h objc.h \ objc-api.h \ ! NXConstStr.h Object.h Protocol.h encoding.h typedstream.h thread.h # copy objc headers to installation include directory *************** *** 99,100 **** --- 99,102 ---- Object.o: Object.m Protocol.o: Protocol.m + thread.o: thread.c thread-solaris.c thread-irix.c thread-win32.c thread-single.c + diff -rc2P objc/README.threads objc-threaded/README.threads *** objc/README.threads Wed Dec 31 19:00:00 1969 --- objc-threaded/README.threads Sun Jan 21 21:28:14 1996 *************** *** 0 **** --- 1,52 ---- + ============================================================================== + README - Wed Nov 29 15:16:24 EST 1995 + ------------------------------------------------------------------------------ + + Limited documentation is available in the THREADS file. + + A simple multiple threaded test program is available in thread-test/. + + This version has been tested on Sun Solaris, SGI Irix, and Windows NT. + It should also work on any single threaded system. + + Thanks go to the following people for help test and debug the library: + + Scott Christley, scottc@ocbi.com + Andrew McCallum, mccallum@cs.rochester.edu + + galen + gchunt@cs.rochester.edu + + Any questions, bug reports, etc should be directed to: + + Scott Christley, scottc@ocbi.com + + Please do not bug Galen with email as he no longer supports the code. + + ============================================================================== + Changes from prior releases (in revered chronological order): + ------------------------------------------------------------------------------ + + * Fixed bug in copy part of sarray_realloc. I had an < which should + have been <=. (Bug report from Scott). + + ------------------------------------------------------------------------------ + + * Support for DEC OSF/1 is definitely broken. My programs always + seg-fault when I link with libpthreads.a. + + * Thread id's are no longer int's, but are instead of type + _objc_thread_t which is typedef'ed from a void *. An invalid thread + id is denoted by NULL and not -1 as before. + + ------------------------------------------------------------------------------ + + * Renamed thread-winnt.c to thread-win32.c to better reflect support + for the API on both Windows NT and Windows 95 platforms. + (Who knows, maybe even Win32s :-). + + * Fixed bugs in Win32 support as per report from Scott Christley. + + * Fixed bug in sarray_get as per report from Scott Christley. + + diff -rc2P objc/THREADS objc-threaded/THREADS *** objc/THREADS Wed Dec 31 19:00:00 1969 --- objc-threaded/THREADS Sun Jan 21 21:34:30 1996 *************** *** 0 **** --- 1,206 ---- + This file describes in little detail the modifications to the + Objective-C runtime needed to make it thread safe. + + First off, kudos to Galen Hunt who is the author of this great work. + + If you have an comments or just want to know where to + send me money to express your undying graditude for threading the + Objective-C runtime you can reach Galen at: + + gchunt@cs.rochester.edu + + Any questions, comments, bug reports, etc. should send email either to the + GCC bug account or to: + + Scott Christley + + ****************************************************************************** + * Simple test program: + + A simple test program can be found in the thread-test directory. + + ****************************************************************************** + * Sarray Threading: + + The most critical component of the Objective-C runtime is the sparse array + structure (sarray). Sarrays store object selectors and implementations. + Following in the tradition of the Objective-C runtime, my threading + support assumes that fast message dispatching is far more important + than *ANY* and *ALL* other operations. The message dispatching thus + uses *NO* locks on any kind. In fact, if you look in sarray.h, you + will notice that the message dispatching has not been modified. + Instead, I have modified the sarray management functions so that all + updates to the sarray data structure can be made in parallel will + message dispatching. + + To support concurrent message dispatching, no dynamically allocated + sarray data structures are freed while more than one thread is + operational. Sarray data structures that are no longer in use are + kept in a linked list of garbage and are released whenever the program + is operating with a single thread. The programmer can also flush the + garbage list by calling sarray_remove_garbage when the programmer can + ensure that no message dispatching is taking place concurrently. The + amount of un-reclaimed sarray garbage should normally be extremely + small in a real program as sarray structures are freed only when using + the "poseAs" functionality and early in program initialization, which + normally occurs while the program is single threaded. + + ****************************************************************************** + * Static Variables: + + The following variables are either statically or globally defined. This list + does not include variables which are internal to implementation dependent + versions of thread-*.c. + + The following threading designations are used: + SAFE : Implicitly thread safe. + SINGLE : Must only be used in single thread mode. + MUTEX : Protected by single global mutex objc_runtime_mutex. + UNUSED : Not used in the runtime. + + Variable Name: Usage: Defined: Also used in: + =========================== ====== ============ ===================== + __objc_class_hash MUTEX class.c + __objc_class_links_resolved UNUSED class.c runtime.h + __objc_class_number MUTEX class.c + __objc_dangling_categories UNUSED init.c + __objc_module_list MUTEX init.c + __objc_selector_array MUTEX selector.c + __objc_selector_hash MUTEX selector.c + __objc_selector_max_index MUTEX selector.c sendmsg.c runtime.h + __objc_selector_names MUTEX selector.c + __objc_thread_exit_status SAFE thread.c + __objc_uninstalled_dtable MUTEX sendmsg.c selector.c + _objc_load_callback SAFE init.c objc-api.h + _objc_lookup_class SAFE class.c objc-api.h + _objc_object_alloc SINGLE objects.c objc-api.h + _objc_object_copy SINGLE objects.c objc-api.h + _objc_object_dispose SINGLE objects.c objc-api.h + frwd_sel SAFE2 sendmsg.c + idxsize MUTEX sarray.c sendmsg.c sarray.h + initialize_sel SAFE2 sendmsg.c + narrays MUTEX sarray.c sendmsg.c sarray.h + nbuckets MUTEX sarray.c sendmsg.c sarray.h + nindices MUTEX sarray.c sarray.h + previous_constructors SAFE1 init.c + proto_class SAFE1 init.c + unclaimed_categories MUTEX init.c + unclaimed_proto_list MUTEX init.c + uninitialized_statics MUTEX init.c + + Notes: + 1) Initialized once in unithread mode. + 2) Initialized value will always be same, guaranteed by lock on selector + hash table. + + ****************************************************************************** + * Linking: + + On Solaris, you must link with -lthread to include the system + thread library. We use its low level thread and mutex implementations. + + On OSF/1, you must link with -lpthreads to include the pthreads library. + + On WIN32, thread support is built-in to the WIN32 API; refer to your + compiler documentation for the appropriate library. + + ****************************************************************************** + * Threads: + + The thread system attempts to create multiple threads using whatever + operating system or library thread support is available. It does + assume that all system functions are thread safe. Notably this means + that the system implementation of malloc and free must be thread safe. + If a system has multiple processors, the threads are configured for + full parallel processing. + + __objc_init_thread_system(void), int + Initialize the thread subsystem. Call once by __objc_exec_class. + + __objc_fini_thread_system(void), int + Closes the thread subsystem. + + objc_thread_detach(SEL selector, id object, id argument), int + Creates and detaches a new thread. The new thread starts by + sending the given selector with a single argument to the + given object. + + objc_thread_set_priority(int priority), int + Sets a threads relative priority within the program. Valid + options are: + + OBJC_THREAD_INTERACTIVE_PRIORITY + OBJC_THREAD_BACKGROUND_PRIORITY + OBJC_THREAD_LOW_PRIORITY + + objc_thread_get_priority(void), int + Query a threads priority. + + objc_thread_yield(void), void + Yields processor to another thread with equal or higher + priority. It is up to the system scheduler to determine if + the processor is taken or not. + + objc_thread_exit(void), int + Terminates a thread. If this is the last thread executing + then the program will terminate. + + objc_thread_id(void), int + Returns the current thread's id. + + objc_thread_set_data(void *value), int + Set a pointer to the thread's local storage. Local storage is + thread specific. + + objc_thread_get_data(void), void * + Returns the pointer to the thread's local storage. + + ****************************************************************************** + * Mutexs: + + Mutexs can be locked recursively. Each mutex locked mutex remembers + its owner (by thread id) and how many times it has been locked. The + last unlock on a mutex removes the system lock and allows other + threads to access the mutex. + + objc_mutex_allocate(void), Mutex_t + Allocates a new mutex. Mutex is initially unlocked. + + objc_mutex_deallocate(Mutex_t mutex), int + Free a mutex. Before freeing the mutex, makes sure that no + one else is using it. + + objc_mutex_lock(Mutex_t mutex), int + Locks a mutex. As mentioned earlier, the same thread may call + this routine repeatedly. + + objc_mutex_trylock(Mutex_t mutex), int + Attempts to lock a mutex. Returns -1 if failed. If lock on + mutex can be acquired then function operates exactly as + objc_mutex_lock. + + objc_mutex_unlock(Mutex_t mutex), int + Unlocks the mutex by one level. Other threads may not acquire + the mutex until this thread has released all locks on it. + + ****************************************************************************** + * Sample run of thread-test/checks/test01.m + + << program started >> -- Program started + __objc_exec_class(Object.m) -- Initialize once + __objc_init_mutex_system + __objc_init_thread_system + __objc_init_selector_tables() + __objc_init_class_tables() + __objc_init_dispatch_tables() + __objc_exec_class(Protocol.m) -- Called repeatedly + __objc_init_protocols(0x000746d4) -- Called repeatedly + class_add_method_list(0x74718, 0x74208) -- Called repeatedly + << main called >> -- Main called + __objc_init_install_dtable(0x6d980, 0x6d5c0) -- Called repeatedly + << delegatePool filled, count=10 >> -- Code in secondary function + __objc_init_install_dtable(0x76268, 0x70614) -- Called repeatedly + Array: count=1 -- More secondary code. + EltNodeCollector: count=1 + << end of program >> -- End of program + diff -rc2P objc/class.c objc-threaded/class.c *** objc/class.c Sat Jan 20 21:09:37 1996 --- objc-threaded/class.c Fri Jan 26 14:41:59 1996 *************** *** 28,41 **** /* The table of classname->class. Used for objc_lookup_class and friends */ ! static cache_ptr __objc_class_hash = 0; /* This is a hook which is called by objc_get_class and objc_lookup_class if the runtime is not able to find the class. This may e.g. try to load in the class using dynamic loading */ ! Class (*_objc_lookup_class)(const char* name) = 0; /* True when class links has been resolved */ ! BOOL __objc_class_links_resolved = NO; --- 28,41 ---- /* The table of classname->class. Used for objc_lookup_class and friends */ ! static cache_ptr __objc_class_hash = 0; /* !T:MUTEX */ /* This is a hook which is called by objc_get_class and objc_lookup_class if the runtime is not able to find the class. This may e.g. try to load in the class using dynamic loading */ ! Class (*_objc_lookup_class)(const char* name) = 0; /* !T:SAFE */ /* True when class links has been resolved */ ! BOOL __objc_class_links_resolved = NO; /* !T:UNUSED */ *************** *** 50,57 **** --- 50,61 ---- 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); } *************** *** 63,66 **** --- 67,72 ---- Class h_class; + objc_mutex_lock(__objc_runtime_mutex); + /* make sure the table is there */ assert(__objc_class_hash); *************** *** 83,86 **** --- 89,94 ---- hash_add (&__objc_class_hash, class->name, class); } + + objc_mutex_unlock(__objc_runtime_mutex); } *************** *** 92,95 **** --- 100,105 ---- Class class; + objc_mutex_lock(__objc_runtime_mutex); + /* Make sure the class hash table exists. */ assert (__objc_class_hash); *************** *** 97,100 **** --- 107,112 ---- class = hash_value_for_key (__objc_class_hash, name); + objc_mutex_unlock(__objc_runtime_mutex); + if (class) return class; *************** *** 114,117 **** --- 126,131 ---- Class class; + objc_mutex_lock(__objc_runtime_mutex); + /* Make sure the class hash table exists. */ assert (__objc_class_hash); *************** *** 119,122 **** --- 133,138 ---- class = hash_value_for_key (__objc_class_hash, name); + objc_mutex_unlock(__objc_runtime_mutex); + if (class) return class; *************** *** 150,153 **** --- 166,171 ---- objc_next_class(void **enum_state) { + objc_mutex_lock(__objc_runtime_mutex); + /* make sure the table is there */ assert(__objc_class_hash); *************** *** 155,158 **** --- 173,179 ---- *(node_ptr*)enum_state = hash_next(__objc_class_hash, *(node_ptr*)enum_state); + + objc_mutex_unlock(__objc_runtime_mutex); + if (*(node_ptr*)enum_state) return (*(node_ptr*)enum_state)->value; *************** *** 170,173 **** --- 191,196 ---- assert(object_class); + objc_mutex_lock(__objc_runtime_mutex); + /* Assign subclass links */ for (node = hash_next (__objc_class_hash, NULL); node; *************** *** 235,238 **** --- 258,263 ---- } } + + objc_mutex_unlock(__objc_runtime_mutex); } *************** *** 308,311 **** --- 333,338 ---- superclass into impostor. */ + objc_mutex_lock(__objc_runtime_mutex); + for (node = hash_next (__objc_class_hash, NULL); node; node = hash_next (__objc_class_hash, node)) *************** *** 317,320 **** --- 344,349 ---- } } + + objc_mutex_unlock(__objc_runtime_mutex); /* next, we update the dispatch tables... */ diff -rc2P objc/init.c objc-threaded/init.c *** objc/init.c Sat Jan 20 21:09:37 1996 --- objc-threaded/init.c Fri Jan 26 14:43:19 1996 *************** *** 32,42 **** /* This list contains all modules currently loaded into the runtime */ ! static struct objc_list* __objc_module_list = 0; /* This list contains all proto_list's not yet assigned class links */ ! static struct objc_list* unclaimed_proto_list = 0; /* List of unresolved static instances. */ ! static struct objc_list *uninitialized_statics; /* Check compiler vs runtime version */ --- 32,48 ---- /* This list contains all modules currently loaded into the runtime */ ! static struct objc_list* __objc_module_list = 0; /* !T:MUTEX */ /* This list contains all proto_list's not yet assigned class links */ ! static struct objc_list* unclaimed_proto_list = 0; /* !T:MUTEX */ /* List of unresolved static instances. */ ! static struct objc_list *uninitialized_statics = 0; /* !T:MUTEX */ ! ! /* Global runtime "write" mutex. */ ! _objc_mutex_t __objc_runtime_mutex; ! ! /* Number of threads that are alive. */ ! int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ /* Check compiler vs runtime version */ *************** *** 53,60 **** dynamic loader determine the classes that have been loaded when an object file is dynamically linked in */ ! void (*_objc_load_callback)(Class class, Category* category) = 0; /* Is all categories/classes resolved? */ ! BOOL __objc_dangling_categories = NO; extern SEL --- 59,66 ---- dynamic loader determine the classes that have been loaded when an object file is dynamically linked in */ ! void (*_objc_load_callback)(Class class, Category* category) = 0; /* !T:SAFE */ /* Is all categories/classes resolved? */ ! BOOL __objc_dangling_categories = NO; /* !T:UNUSED */ extern SEL *************** *** 70,73 **** --- 76,81 ---- struct objc_static_instances **statics_in_module; + objc_mutex_lock(__objc_runtime_mutex); + while (*cell) { *************** *** 115,118 **** --- 123,128 ---- cell = &(*cell)->tail; } + + objc_mutex_unlock(__objc_runtime_mutex); } /* objc_init_statics */ *************** *** 151,154 **** --- 161,169 ---- if (!previous_constructors) { + /* Initialize thread-safe system */ + __objc_init_thread_system(); + __objc_runtime_threads_alive = 1; + __objc_runtime_mutex = objc_mutex_allocate(); + __objc_init_selector_tables(); __objc_init_class_tables(); *************** *** 158,161 **** --- 173,177 ---- /* Save the module pointer for later processing. (not currently used) */ + objc_mutex_lock(__objc_runtime_mutex); __objc_module_list = list_cons(module, __objc_module_list); *************** *** 288,291 **** --- 304,308 ---- } + objc_mutex_unlock(__objc_runtime_mutex); } *************** *** 316,319 **** --- 333,338 ---- return; + objc_mutex_lock(__objc_runtime_mutex); + if (!proto_class) proto_class = objc_lookup_class("Protocol"); *************** *** 349,352 **** --- 368,373 ---- } } + + objc_mutex_unlock(__objc_runtime_mutex); } Only in objc: list.h diff -rc2P objc/makefile.dos objc-threaded/makefile.dos *** objc/makefile.dos Sat Jan 20 21:09:38 1996 --- objc-threaded/makefile.dos Sat Jan 20 22:13:18 1996 *************** *** 38,42 **** OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o \ ! selector.o objects.o misc.o object.o protocol.o encoding.o libobjc.a: $(OBJC_O) --- 38,42 ---- OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o \ ! selector.o objects.o misc.o object.o protocol.o encoding.o thread.o libobjc.a: $(OBJC_O) *************** *** 45,52 **** ranlib libobjc.a ! OBJC_H = hash.h list.h sarray.h objc.h \ objc-api.h \ object.h protocol.h mutex.h \ ! typedstream.h mostlyclean: --- 45,52 ---- ranlib libobjc.a ! OBJC_H = hash.h objc-list.h sarray.h objc.h \ objc-api.h \ object.h protocol.h mutex.h \ ! typedstream.h thread.h mostlyclean: diff -rc2P objc/objc-api.h objc-threaded/objc-api.h *** objc/objc-api.h Sat Jan 20 21:09:38 1996 --- objc-threaded/objc-api.h Fri Jan 26 14:44:31 1996 *************** *** 30,33 **** --- 30,34 ---- #include "objc/objc.h" #include "objc/hash.h" + #include "objc/thread.h" #include diff -rc2P objc/objc-list.h objc-threaded/objc-list.h *** objc/objc-list.h Wed Dec 31 19:00:00 1969 --- objc-threaded/objc-list.h Thu Jun 15 08:40:27 1995 *************** *** 0 **** --- 1,150 ---- + /* Generic single linked list to keep various information + Copyright (C) 1993, 1994 Free Software Foundation, Inc. + + Author: Kresten Krab Thorup + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) + any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License + along with GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + #ifndef __GNU_OBJC_LIST_H + #define __GNU_OBJC_LIST_H + void * __objc_xrealloc (void *optr, size_t size); + void * __objc_xmalloc (size_t size); + + struct objc_list { + void *head; + struct objc_list *tail; + }; + + /* Return a cons cell produced from (head . tail) */ + + static inline struct objc_list* + list_cons(void* head, struct objc_list* tail) + { + struct objc_list* cell; + + cell = (struct objc_list*)__objc_xmalloc(sizeof(struct objc_list)); + cell->head = head; + cell->tail = tail; + return cell; + } + + /* Return the length of a list, list_length(NULL) returns zero */ + + static inline int + list_length(struct objc_list* list) + { + int i = 0; + while(list) + { + i += 1; + list = list->tail; + } + return i; + } + + /* Return the Nth element of LIST, where N count from zero. If N + larger than the list length, NULL is returned */ + + static inline void* + list_nth(int index, struct objc_list* list) + { + while(index-- != 0) + { + if(list->tail) + list = list->tail; + else + return 0; + } + return list->head; + } + + /* Remove the element at the head by replacing it by its successor */ + + static inline void + list_remove_head(struct objc_list** list) + { + if ((*list)->tail) + { + struct objc_list* tail = (*list)->tail; /* fetch next */ + *(*list) = *tail; /* copy next to list head */ + free(tail); /* free next */ + } + else /* only one element in list */ + { + free (*list); + (*list) = 0; + } + } + + + /* Remove the element with `car' set to ELEMENT */ + + static inline void + list_remove_elem(struct objc_list** list, void* elem) + { + while (*list) { + if ((*list)->head == elem) + list_remove_head(list); + list = &((*list)->tail); + } + } + + /* Map FUNCTION over all elements in LIST */ + + static inline void + list_mapcar(struct objc_list* list, void(*function)(void*)) + { + while(list) + { + (*function)(list->head); + list = list->tail; + } + } + + /* Return element that has ELEM as car */ + + static inline struct objc_list** + list_find(struct objc_list** list, void* elem) + { + while(*list) + { + if ((*list)->head == elem) + return list; + list = &((*list)->tail); + } + return NULL; + } + + /* Free list (backwards recursive) */ + + static void + list_free(struct objc_list* list) + { + if(list) + { + list_free(list->tail); + free(list); + } + } + #endif __GNU_OBJC_LIST_H diff -rc2P objc/objects.c objc-threaded/objects.c *** objc/objects.c Sat Jan 20 21:09:38 1996 --- objc-threaded/objects.c Sun Jan 21 16:46:14 1996 *************** *** 32,38 **** id __objc_object_copy(id); ! id (*_objc_object_alloc)(Class) = __objc_object_alloc; ! id (*_objc_object_dispose)(id) = __objc_object_dispose; ! id (*_objc_object_copy)(id) = __objc_object_copy; id --- 32,38 ---- id __objc_object_copy(id); ! id (*_objc_object_alloc)(Class) = __objc_object_alloc; /* !T:SINGLE */ ! id (*_objc_object_dispose)(id) = __objc_object_dispose; /* !T:SINGLE */ ! id (*_objc_object_copy)(id) = __objc_object_copy; /* !T:SINGLE */ id diff -rc2P objc/runtime.h objc-threaded/runtime.h *** objc/runtime.h Sat Jan 20 21:09:38 1996 --- objc-threaded/runtime.h Fri Jan 26 14:46:27 1996 *************** *** 38,43 **** #include "objc/objc-api.h" /* runtime api functions */ #include "objc/hash.h" /* hash structures */ ! #include "objc/list.h" /* linear lists */ extern void __objc_add_class_to_hash(Class); /* (objc-class.c) */ --- 38,45 ---- #include "objc/objc-api.h" /* runtime api functions */ + #include "objc/thread.h" /* thread and mutex support */ + #include "objc/hash.h" /* hash structures */ ! #include "objc/objc-list.h" /* linear lists */ extern void __objc_add_class_to_hash(Class); /* (objc-class.c) */ *************** *** 49,52 **** --- 51,58 ---- extern void __objc_register_selectors_from_class(Class); /* (objc-sel.c) */ extern void __objc_update_dispatch_table_for_class (Class);/* (objc-msg.c) */ + + extern int __objc_init_thread_system(void); /* thread.c */ + extern int __objc_fini_thread_system(void); /* thread.c */ + extern void class_add_method_list(Class, MethodList_t); *************** *** 59,62 **** --- 65,74 ---- /* Number of selectors stored in each of the selector tables */ extern int __objc_selector_max_index; + + /* Mutex locking __objc_selector_max_index and its arrays. */ + extern _objc_mutex_t __objc_runtime_mutex; + + /* Number of threads which are alive. */ + extern int __objc_runtime_threads_alive; #ifdef DEBUG diff -rc2P objc/sarray.c objc-threaded/sarray.c *** objc/sarray.c Sat Jan 20 21:09:38 1996 --- objc-threaded/sarray.c Fri Jan 26 14:52:16 1996 *************** *** 26,36 **** #include "objc/sarray.h" #include #include "assert.h" ! int nbuckets = 0; ! int nindices = 0; ! int narrays = 0; ! int idxsize = 0; #ifdef OBJC_SPARSE2 --- 26,39 ---- #include "objc/sarray.h" + #include "objc/runtime.h" #include #include "assert.h" ! int nbuckets = 0; /* !T:MUTEX */ ! int nindices = 0; /* !T:MUTEX */ ! int narrays = 0; /* !T:MUTEX */ ! int idxsize = 0; /* !T:MUTEX */ ! ! static void * first_free_data = NULL; /* !T:MUTEX */ #ifdef OBJC_SPARSE2 *************** *** 47,50 **** --- 50,98 ---- #endif + /* 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; + free(vp); + vp = np; + } + + objc_mutex_unlock(__objc_runtime_mutex); + } + + /* Free a block of dynamically allocated memory. If we are in multi-threaded + mode, it is ok to free it. If not, we add it to the garbage heap to be + freed later. */ + + static void + sarray_free_garbage(void *vp) + { + objc_mutex_lock(__objc_runtime_mutex); + + if (__objc_runtime_threads_alive == 1) { + 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); + } + + /* sarray_at_put : copies data in such a way as to be thread reader safe. */ void sarray_at_put(struct sarray* array, sidx index, void* element) *************** *** 52,57 **** --- 100,107 ---- #ifdef OBJC_SPARSE3 struct sindex** the_index; + struct sindex* new_index; #endif struct sbucket** the_bucket; + struct sbucket* new_bucket; #ifdef OBJC_SPARSE3 size_t ioffset; *************** *** 97,116 **** /* The index was previously empty, allocate a new */ ! *the_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); ! memcpy(*the_index, array->empty_index, sizeof(struct sindex)); ! (*the_index)->version = array->version; the_bucket = &((*the_index)->buckets[boffset]); - nindices += 1; ! } else if ((*the_index)->version != array->version) { /* This index must be lazy copied */ struct sindex* old_index = *the_index; ! *the_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); ! memcpy( *the_index,old_index, sizeof(struct sindex)); ! (*the_index)->version = array->version; the_bucket = &((*the_index)->buckets[boffset]); - nindices += 1; } --- 147,168 ---- /* The index was previously empty, allocate a new */ ! new_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); ! memcpy(new_index, array->empty_index, sizeof(struct sindex)); ! new_index->version.version = array->version.version; ! *the_index = new_index; /* Prepared for install. */ the_bucket = &((*the_index)->buckets[boffset]); ! nindices += 1; ! } else if ((*the_index)->version.version != array->version.version) { /* This index must be lazy copied */ struct sindex* old_index = *the_index; ! new_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); ! memcpy( new_index, old_index, sizeof(struct sindex)); ! new_index->version.version = array->version.version; ! *the_index = new_index; /* Prepared for install. */ the_bucket = &((*the_index)->buckets[boffset]); + nindices += 1; } *************** *** 123,138 **** /* The bucket was previously empty (or something like that), */ /* allocate a new. This is the effect of `lazy' allocation */ ! *the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); ! memcpy((void *) *the_bucket, (const void*)array->empty_bucket, sizeof(struct sbucket)); ! (*the_bucket)->version = array->version; nbuckets += 1; ! } else if ((*the_bucket)->version != array->version) { /* Perform lazy copy. */ struct sbucket* old_bucket = *the_bucket; ! *the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); ! memcpy( *the_bucket,old_bucket, sizeof(struct sbucket)); ! (*the_bucket)->version = array->version; nbuckets += 1; --- 175,194 ---- /* The bucket was previously empty (or something like that), */ /* allocate a new. This is the effect of `lazy' allocation */ ! new_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); ! memcpy((void *) new_bucket, (const void*)array->empty_bucket, sizeof(struct sbucket)); ! new_bucket->version.version = array->version.version; ! *the_bucket = new_bucket; /* Prepared for install. */ ! nbuckets += 1; ! } else if ((*the_bucket)->version.version != array->version.version) { /* Perform lazy copy. */ struct sbucket* old_bucket = *the_bucket; ! new_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); ! memcpy( new_bucket, old_bucket, sizeof(struct sbucket)); ! new_bucket->version.version = array->version.version; ! *the_bucket = new_bucket; /* Prepared for install. */ ! nbuckets += 1; *************** *** 152,162 **** sarray_new (int size, void* default_element) { #ifdef OBJC_SPARSE3 size_t num_indices = ((size-1)/(INDEX_CAPACITY))+1; #else /* OBJC_SPARSE2 */ size_t num_indices = ((size-1)/BUCKET_SIZE)+1; #endif int counter; - struct sarray* arr; assert(size > 0); --- 208,220 ---- sarray_new (int size, void* default_element) { + struct sarray* arr; #ifdef OBJC_SPARSE3 size_t num_indices = ((size-1)/(INDEX_CAPACITY))+1; + struct sindex ** new_indices; #else /* OBJC_SPARSE2 */ size_t num_indices = ((size-1)/BUCKET_SIZE)+1; + struct sbucket ** new_buckets; #endif int counter; assert(size > 0); *************** *** 164,185 **** /* Allocate core array */ arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); ! arr->version = 0; ! narrays += 1; /* Initialize members */ #ifdef OBJC_SPARSE3 arr->capacity = num_indices*INDEX_CAPACITY; ! arr->indices = (struct sindex**) __objc_xmalloc(sizeof(struct sindex*)*num_indices); - idxsize += num_indices; arr->empty_index = (struct sindex*) __objc_xmalloc(sizeof(struct sindex)); ! arr->empty_index->version = 0; nindices += 1; #else /* OBJC_SPARSE2 */ arr->capacity = num_indices*BUCKET_SIZE; ! arr->buckets = (struct sbucket**) __objc_xmalloc(sizeof(struct sbucket*)*num_indices); idxsize += num_indices; --- 222,246 ---- /* Allocate core array */ arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); ! arr->version.version = 0; /* Initialize members */ #ifdef OBJC_SPARSE3 arr->capacity = num_indices*INDEX_CAPACITY; ! new_indices = (struct sindex**) __objc_xmalloc(sizeof(struct sindex*)*num_indices); arr->empty_index = (struct sindex*) __objc_xmalloc(sizeof(struct sindex)); ! arr->empty_index->version.version = 0; ! ! narrays += 1; ! idxsize += num_indices; nindices += 1; #else /* OBJC_SPARSE2 */ arr->capacity = num_indices*BUCKET_SIZE; ! new_buckets = (struct sbucket**) __objc_xmalloc(sizeof(struct sbucket*)*num_indices); + + narrays += 1; idxsize += num_indices; *************** *** 187,191 **** arr->empty_bucket = (struct sbucket*) __objc_xmalloc(sizeof(struct sbucket)); ! arr->empty_bucket->version = 0; nbuckets += 1; --- 248,253 ---- arr->empty_bucket = (struct sbucket*) __objc_xmalloc(sizeof(struct sbucket)); ! arr->empty_bucket->version.version = 0; ! nbuckets += 1; *************** *** 201,218 **** for (counter=0; counterindices[counter] = arr->empty_index; #else /* OBJC_SPARSE2 */ for (counter=0; counterbuckets[counter] = arr->empty_bucket; #endif ! return arr; } ! /* Reallocate the sparse array to hold `newsize' entries */ void --- 263,288 ---- for (counter=0; counterempty_index; #else /* OBJC_SPARSE2 */ for (counter=0; counterempty_bucket; #endif ! ! #ifdef OBJC_SPARSE3 ! arr->indices = new_indices; ! #else /* OBJC_SPARSE2 */ ! arr->buckets = new_buckets; ! #endif ! return arr; } ! /* Reallocate the sparse array to hold `newsize' entries ! Note: We really allocate and then free. We have to do this to ensure that ! any concurrent readers notice the update. */ void *************** *** 224,227 **** --- 294,300 ---- size_t rounded_size = (new_max_index+1)*INDEX_CAPACITY; + struct sindex ** new_indices; + struct sindex ** old_indices; + #else /* OBJC_SPARSE2 */ size_t old_max_index = (array->capacity-1)/BUCKET_SIZE; *************** *** 229,232 **** --- 302,308 ---- size_t rounded_size = (new_max_index+1)*BUCKET_SIZE; + struct sbucket ** new_buckets; + struct sbucket ** old_buckets; + #endif *************** *** 236,320 **** /* The size is the same, just ignore the request */ ! if(rounded_size == array->capacity) return; assert(array->ref_count == 1); /* stop if lazy copied... */ ! if(rounded_size < array->capacity) { - /* update capacity */ - array->capacity = rounded_size; - /* free buckets above new_max_index */ - for(counter = old_max_index; counter > new_max_index; counter-- ) { #ifdef OBJC_SPARSE3 ! struct sindex* idx = array->indices[counter]; ! if((idx != array->empty_index) && (idx->version == array->version)) { ! int c2; ! for(c2=0; c2buckets[c2]; ! if((bkt != array->empty_bucket) && (bkt->version == array->version)) ! { ! free(bkt); ! nbuckets -= 1; ! } ! } ! free(idx); ! nindices -= 1; ! } #else /* OBJC_SPARSE2 */ ! struct sbucket* bkt = array->buckets[counter]; ! if ((bkt != array->empty_bucket) && (bkt->version == array->version)) ! { ! free(bkt); ! nbuckets -= 1; ! } #endif ! } ! ! #ifdef OBJC_SPARSE3 ! /* realloc to free the space above new_max_index */ ! array->indices = (struct sindex**) ! __objc_xrealloc(array->indices, ! (new_max_index+1)*sizeof(struct sindex*)); ! #else /* OBJC_SPARSE2 */ ! array->buckets = (struct sbucket**) ! __objc_xrealloc(array->buckets, ! (new_max_index+1)*sizeof(struct sbucket*)); ! #endif ! idxsize -= (old_max_index-new_max_index); ! ! return; ! } ! ! /* We are asked to extend the array -- reallocate the bucket table, */ ! /* and insert empty_bucket in newly allocated places. */ ! if(rounded_size > array->capacity) ! { /* update capacity */ array->capacity = rounded_size; #ifdef OBJC_SPARSE3 ! /* realloc to make room in table above old_max_index */ ! array->indices = (struct sindex**) ! __objc_xrealloc(array->indices, ! (new_max_index+1)*sizeof(struct sindex*)); /* reset entries above old_max_index to empty_bucket */ for(counter = old_max_index+1; counter <= new_max_index; counter++) ! array->indices[counter] = array->empty_index; ! #else /* OBJC_SPARSE2 */ - - /* realloc to make room in table above old_max_index */ - array->buckets = (struct sbucket**) - __objc_xrealloc(array->buckets, - (new_max_index+1)*sizeof(struct sbucket*)); - /* reset entries above old_max_index to empty_bucket */ for(counter = old_max_index+1; counter <= new_max_index; counter++) ! array->buckets[counter] = array->empty_bucket; #endif idxsize += (new_max_index-old_max_index); return; --- 312,381 ---- /* The size is the same, just ignore the request */ ! if(rounded_size <= array->capacity) return; assert(array->ref_count == 1); /* stop if lazy copied... */ ! /* We are asked to extend the array -- allocate new bucket table, */ ! /* and insert empty_bucket in newly allocated places. */ ! if(rounded_size > array->capacity) { #ifdef OBJC_SPARSE3 ! new_max_index += 4; ! rounded_size = (new_max_index+1)*INDEX_CAPACITY; ! #else /* OBJC_SPARSE2 */ ! new_max_index += 4; ! rounded_size = (new_max_index+1)*BUCKET_SIZE; #endif ! /* update capacity */ array->capacity = rounded_size; #ifdef OBJC_SPARSE3 ! /* alloc to force re-read by any concurrent readers. */ ! old_indices = array->indices; ! new_indices = (struct sindex**) ! __objc_xmalloc((new_max_index+1)*sizeof(struct sindex*)); ! #else /* OBJC_SPARSE2 */ ! old_buckets = array->buckets; ! new_buckets = (struct sbucket**) ! __objc_xmalloc((new_max_index+1)*sizeof(struct sbucket*)); ! #endif + /* copy buckets below old_max_index (they are still valid) */ + for(counter = 0; counter <= old_max_index; counter++ ) { + #ifdef OBJC_SPARSE3 + new_indices[counter] = old_indices[counter]; + #else /* OBJC_SPARSE2 */ + new_buckets[counter] = old_buckets[counter]; + #endif + } + + #ifdef OBJC_SPARSE3 /* reset entries above old_max_index to empty_bucket */ for(counter = old_max_index+1; counter <= new_max_index; counter++) ! new_indices[counter] = array->empty_index; #else /* OBJC_SPARSE2 */ /* reset entries above old_max_index to empty_bucket */ for(counter = old_max_index+1; counter <= new_max_index; counter++) ! new_buckets[counter] = array->empty_bucket; ! #endif ! ! #ifdef OBJC_SPARSE3 ! /* install the new indices */ ! array->indices = new_indices; ! #else /* OBJC_SPARSE2 */ ! array->buckets = new_buckets; ! #endif + #ifdef OBJC_SPARSE3 + /* free the old indices */ + sarray_free_garbage(old_indices); + #else /* OBJC_SPARSE2 */ + sarray_free_garbage(old_buckets); #endif + idxsize += (new_max_index-old_max_index); return; *************** *** 327,334 **** --- 388,398 ---- void sarray_free(struct sarray* array) { + #ifdef OBJC_SPARSE3 size_t old_max_index = (array->capacity-1)/INDEX_CAPACITY; + struct sindex ** old_indices; #else size_t old_max_index = (array->capacity-1)/BUCKET_SIZE; + struct sbucket ** old_buckets; #endif int counter = 0; *************** *** 339,342 **** --- 403,412 ---- return; + #ifdef OBJC_SPARSE3 + old_indices = array->indices; + #else + old_buckets = array->buckets; + #endif + if((array->is_copy_of) && ((array->is_copy_of->ref_count - 1) == 0)) sarray_free(array->is_copy_of); *************** *** 345,367 **** for(counter = 0; counter <= old_max_index; counter++ ) { #ifdef OBJC_SPARSE3 ! struct sindex* idx = array->indices[counter]; ! if((idx != array->empty_index) && (idx->version == array->version)) { int c2; for(c2=0; c2buckets[c2]; ! if((bkt != array->empty_bucket) && (bkt->version == array->version)) { ! free(bkt); nbuckets -= 1; } } ! free(idx); nindices -= 1; } #else /* OBJC_SPARSE2 */ struct sbucket* bkt = array->buckets[counter]; ! if ((bkt != array->empty_bucket) && (bkt->version == array->version)) { ! free(bkt); nbuckets -= 1; } --- 415,440 ---- for(counter = 0; counter <= old_max_index; counter++ ) { #ifdef OBJC_SPARSE3 ! struct sindex* idx = old_indices[counter]; ! if((idx != array->empty_index) && ! (idx->version.version == array->version.version)) { int c2; for(c2=0; c2buckets[c2]; ! if((bkt != array->empty_bucket) && ! (bkt->version.version == array->version.version)) { ! sarray_free_garbage(bkt); nbuckets -= 1; } } ! sarray_free_garbage(idx); nindices -= 1; } #else /* OBJC_SPARSE2 */ struct sbucket* bkt = array->buckets[counter]; ! if ((bkt != array->empty_bucket) && ! (bkt->version.version == array->version.version)) { ! sarray_free_garbage(bkt); nbuckets -= 1; } *************** *** 371,376 **** #ifdef OBJC_SPARSE3 /* free empty_index */ ! if(array->empty_index->version == array->version) { ! free(array->empty_index); nindices -= 1; } --- 444,449 ---- #ifdef OBJC_SPARSE3 /* free empty_index */ ! if(array->empty_index->version.version == array->version.version) { ! sarray_free_garbage(array->empty_index); nindices -= 1; } *************** *** 378,401 **** /* free empty_bucket */ ! if(array->empty_bucket->version == array->version) { ! free(array->empty_bucket); nbuckets -= 1; } #ifdef OBJC_SPARSE3 /* free bucket table */ ! free(array->indices); ! idxsize -= (old_max_index+1); #else /* free bucket table */ ! free(array->buckets); ! idxsize -= (old_max_index+1); #endif ! /* free array */ ! free(array); ! narrays -= 1; } --- 451,473 ---- /* free empty_bucket */ ! if(array->empty_bucket->version.version == array->version.version) { ! sarray_free_garbage(array->empty_bucket); nbuckets -= 1; } + idxsize -= (old_max_index+1); + narrays -= 1; #ifdef OBJC_SPARSE3 /* free bucket table */ ! sarray_free_garbage(array->indices); #else /* free bucket table */ ! sarray_free_garbage(array->buckets); #endif ! /* free array */ ! sarray_free_garbage(array); } *************** *** 406,441 **** sarray_lazy_copy(struct sarray* oarr) { #ifdef OBJC_SPARSE3 size_t num_indices = ((oarr->capacity-1)/INDEX_CAPACITY)+1; #else /* OBJC_SPARSE2 */ size_t num_indices = ((oarr->capacity-1)/BUCKET_SIZE)+1; #endif - struct sarray* arr; /* Allocate core array */ ! arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); ! memcpy( arr,oarr, sizeof(struct sarray)); ! arr->version = oarr->version + 1; ! arr->is_copy_of = oarr; ! oarr->ref_count += 1; arr->ref_count = 1; #ifdef OBJC_SPARSE3 /* Copy bucket table */ ! arr->indices = (struct sindex**) __objc_xmalloc(sizeof(struct sindex*)*num_indices); ! memcpy( arr->indices,oarr->indices, sizeof(struct sindex*)*num_indices); #else /* Copy bucket table */ ! arr->buckets = (struct sbucket**) __objc_xmalloc(sizeof(struct sbucket*)*num_indices); ! memcpy( arr->buckets,oarr->buckets, sizeof(struct sbucket*)*num_indices); #endif idxsize += num_indices; narrays += 1; ! return arr; } --- 478,522 ---- sarray_lazy_copy(struct sarray* oarr) { + struct sarray* arr; + #ifdef OBJC_SPARSE3 size_t num_indices = ((oarr->capacity-1)/INDEX_CAPACITY)+1; + struct sindex ** new_indices; #else /* OBJC_SPARSE2 */ size_t num_indices = ((oarr->capacity-1)/BUCKET_SIZE)+1; + struct sbucket ** new_buckets; #endif /* Allocate core array */ ! arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); /* !!! */ ! arr->version.version = oarr->version.version + 1; ! #ifdef OBJC_SPARSE3 ! arr->empty_index = oarr->empty_index; ! #endif ! arr->empty_bucket = oarr->empty_bucket; arr->ref_count = 1; + oarr->ref_count += 1; + arr->is_copy_of = oarr; + arr->capacity = oarr->capacity; #ifdef OBJC_SPARSE3 /* Copy bucket table */ ! new_indices = (struct sindex**) __objc_xmalloc(sizeof(struct sindex*)*num_indices); ! memcpy( new_indices,oarr->indices, sizeof(struct sindex*)*num_indices); + arr->indices = new_indices; #else /* Copy bucket table */ ! new_buckets = (struct sbucket**) __objc_xmalloc(sizeof(struct sbucket*)*num_indices); ! memcpy( new_buckets,oarr->buckets, sizeof(struct sbucket*)*num_indices); + arr->buckets = new_buckets; #endif idxsize += num_indices; narrays += 1; ! return arr; } diff -rc2P objc/sarray.h objc-threaded/sarray.h *** objc/sarray.h Sat Jan 20 21:09:38 1996 --- objc-threaded/sarray.h Fri Jan 26 14:50:39 1996 *************** *** 43,46 **** --- 43,48 ---- #include + #include "objc/thread.h" + extern int nbuckets; /* for stats */ extern int nindices; *************** *** 112,118 **** void * __objc_xmalloc (size_t size); struct sbucket { void* elems[BUCKET_SIZE]; /* elements stored in array */ ! short version; /* used for copy-on-write */ }; --- 114,125 ---- void * __objc_xmalloc (size_t size); + union sversion { + int version; + void *next_free; + }; + struct sbucket { void* elems[BUCKET_SIZE]; /* elements stored in array */ ! union sversion version; /* used for copy-on-write */ }; *************** *** 121,125 **** struct sindex { struct sbucket* buckets[INDEX_SIZE]; ! short version; }; --- 128,132 ---- struct sindex { struct sbucket* buckets[INDEX_SIZE]; ! union sversion version; /* used for copy-on-write */ }; *************** *** 134,138 **** #endif /* OBJC_SPARSE2 */ struct sbucket* empty_bucket; ! short version; short ref_count; struct sarray* is_copy_of; --- 141,145 ---- #endif /* OBJC_SPARSE2 */ struct sbucket* empty_bucket; ! union sversion version; /* used for copy-on-write */ short ref_count; struct sarray* is_copy_of; *************** *** 143,150 **** void sarray_free(struct sarray*); struct sarray* sarray_lazy_copy(struct sarray*); - struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */ void sarray_realloc(struct sarray*, int new_size); void sarray_at_put(struct sarray*, sidx index, void* elem); void sarray_at_put_safe(struct sarray*, sidx index, void* elem); --- 150,159 ---- void sarray_free(struct sarray*); struct sarray* sarray_lazy_copy(struct sarray*); void sarray_realloc(struct sarray*, int new_size); void sarray_at_put(struct sarray*, sidx index, void* elem); void sarray_at_put_safe(struct sarray*, sidx index, void* elem); + + struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */ + void sarray_remove_garbage(void); diff -rc2P objc/selector.c objc-threaded/selector.c *** objc/selector.c Sat Jan 20 21:09:38 1996 --- objc-threaded/selector.c Fri Jan 26 14:49:35 1996 *************** *** 32,43 **** /* Tables mapping selector names to uid and opposite */ ! static struct sarray* __objc_selector_array = 0; /* uid -> sel */ ! static struct sarray* __objc_selector_names = 0; /* uid -> name */ ! static cache_ptr __objc_selector_hash = 0; /* name -> uid */ static void register_selectors_from_list(MethodList_t); /* Number of selectors stored in each of the above tables */ ! int __objc_selector_max_index = 0; void __objc_init_selector_tables() --- 32,43 ---- /* Tables mapping selector names to uid and opposite */ ! static struct sarray* __objc_selector_array = 0; /* uid -> sel !T:MUTEX */ ! static struct sarray* __objc_selector_names = 0; /* uid -> name !T:MUTEX */ ! static cache_ptr __objc_selector_hash = 0; /* name -> uid !T:MUTEX */ static void register_selectors_from_list(MethodList_t); /* Number of selectors stored in each of the above tables */ ! int __objc_selector_max_index = 0; /* !T:MUTEX */ void __objc_init_selector_tables() *************** *** 123,129 **** sidx i; i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (i == 0) ! return 0; for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); --- 123,134 ---- sidx i; + objc_mutex_lock(__objc_runtime_mutex); + i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (i == 0) ! { ! objc_mutex_unlock(__objc_runtime_mutex); ! return 0; ! } for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); *************** *** 135,138 **** --- 140,144 ---- if (s->sel_types == types) { + objc_mutex_unlock(__objc_runtime_mutex); return s; } *************** *** 140,147 **** --- 146,155 ---- else if (sel_types_match (s->sel_types, types)) { + objc_mutex_unlock(__objc_runtime_mutex); return s; } } + objc_mutex_unlock(__objc_runtime_mutex); return 0; } *************** *** 155,161 **** SEL s; i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (i == 0) ! return 0; for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); --- 163,174 ---- SEL s; + objc_mutex_lock(__objc_runtime_mutex); + i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (i == 0) ! { ! objc_mutex_unlock(__objc_runtime_mutex); ! return 0; ! } for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); *************** *** 164,170 **** s = (SEL) l->head; if (s->sel_types) ! return s; } return s; } --- 177,187 ---- s = (SEL) l->head; if (s->sel_types) ! { ! objc_mutex_unlock(__objc_runtime_mutex); ! return s; ! } } + objc_mutex_unlock(__objc_runtime_mutex); return s; } *************** *** 177,185 **** sidx i; i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (soffset_decode (i) == 0) ! return 0; l = (struct objc_list*)sarray_get (__objc_selector_array, i); if (l == 0) return 0; --- 194,209 ---- sidx i; + objc_mutex_lock(__objc_runtime_mutex); + i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (soffset_decode (i) == 0) ! { ! objc_mutex_unlock(__objc_runtime_mutex); ! return 0; ! } l = (struct objc_list*)sarray_get (__objc_selector_array, i); + objc_mutex_unlock(__objc_runtime_mutex); + if (l == 0) return 0; *************** *** 200,208 **** sel_get_name (SEL selector) { if ((soffset_decode((sidx)selector->sel_id) > 0) && (soffset_decode((sidx)selector->sel_id) <= __objc_selector_max_index)) ! return sarray_get (__objc_selector_names, (sidx) selector->sel_id); else ! return 0; } --- 224,237 ---- sel_get_name (SEL selector) { + const char *ret; + + objc_mutex_lock(__objc_runtime_mutex); if ((soffset_decode((sidx)selector->sel_id) > 0) && (soffset_decode((sidx)selector->sel_id) <= __objc_selector_max_index)) ! ret = sarray_get (__objc_selector_names, (sidx) selector->sel_id); else ! ret = 0; ! objc_mutex_unlock(__objc_runtime_mutex); ! return ret; } *************** *** 228,232 **** /* Store the passed selector name in the selector record and return its ! selector value (value returned by sel_get_uid). */ SEL __sel_register_typed_name (const char *name, const char *types, --- 257,262 ---- /* Store the passed selector name in the selector record and return its ! selector value (value returned by sel_get_uid). ! Assumes that the calling function has locked down __objc_runtime_mutex. */ SEL __sel_register_typed_name (const char *name, const char *types, *************** *** 311,315 **** sel_register_name (const char *name) { ! return __sel_register_typed_name (name, 0, 0); } --- 341,351 ---- sel_register_name (const char *name) { ! SEL ret; ! ! objc_mutex_lock(__objc_runtime_mutex); ! ret = __sel_register_typed_name (name, 0, 0); ! objc_mutex_unlock(__objc_runtime_mutex); ! ! return ret; } *************** *** 317,321 **** sel_register_typed_name (const char *name, const char *type) { ! return __sel_register_typed_name (name, type, 0); } --- 353,363 ---- sel_register_typed_name (const char *name, const char *type) { ! SEL ret; ! ! objc_mutex_lock(__objc_runtime_mutex); ! ret = __sel_register_typed_name (name, type, 0); ! objc_mutex_unlock(__objc_runtime_mutex); ! ! return ret; } diff -rc2P objc/sendmsg.c objc-threaded/sendmsg.c *** objc/sendmsg.c Sat Jan 20 21:09:38 1996 --- objc-threaded/sendmsg.c Fri Jan 26 15:09:21 1996 *************** *** 41,45 **** /* The uninstalled dispatch table */ ! struct sarray* __objc_uninstalled_dtable = 0; /* Send +initialize to class */ --- 41,45 ---- /* The uninstalled dispatch table */ ! struct sarray* __objc_uninstalled_dtable = 0; /* !T:MUTEX */ /* Send +initialize to class */ *************** *** 77,81 **** --- 77,83 ---- if(res == __objc_init_install_dtable) { + objc_mutex_lock(__objc_runtime_mutex); __objc_install_dispatch_table_for_class (class); + objc_mutex_unlock(__objc_runtime_mutex); res = sarray_get (class->dtable, (size_t) sel->sel_id); } *************** *** 97,101 **** --- 99,105 ---- if(res == __objc_init_install_dtable) { + objc_mutex_lock(__objc_runtime_mutex); __objc_install_dispatch_table_for_class (object->class_pointer); + objc_mutex_unlock(__objc_runtime_mutex); res = sarray_get (object->class_pointer->dtable, (size_t) sel->sel_id); } *************** *** 173,176 **** --- 177,182 ---- goto already_initialized; + objc_mutex_lock(__objc_runtime_mutex); + if(CLS_ISCLASS(receiver->class_pointer)) { *************** *** 199,202 **** --- 205,209 ---- CLS_SETINITIALIZED((Class)receiver); } + objc_mutex_unlock(__objc_runtime_mutex); already_initialized: *************** *** 270,273 **** --- 277,281 ---- } + /* Assumes that __objc_runtime_mutex is locked down. */ static void __objc_install_dispatch_table_for_class (Class class) *************** *** 290,294 **** --- 298,304 ---- if (super == 0) { + objc_mutex_lock(__objc_runtime_mutex); class->dtable = sarray_new (__objc_selector_max_index, 0); + objc_mutex_unlock(__objc_runtime_mutex); } else *************** *** 312,315 **** --- 322,326 ---- { Class next; + struct sarray *arr; /* not yet installed -- skip it */ *************** *** 317,322 **** return; ! sarray_free (class->dtable); /* release memory */ __objc_install_premature_dtable (class); /* someone might require it... */ __objc_install_dispatch_table_for_class (class); /* could have been lazy... */ --- 328,337 ---- return; ! objc_mutex_lock(__objc_runtime_mutex); ! ! arr = class->dtable; __objc_install_premature_dtable (class); /* someone might require it... */ + sarray_free (arr); /* release memory */ + __objc_install_dispatch_table_for_class (class); /* could have been lazy... */ *************** *** 325,328 **** --- 340,344 ---- __objc_update_dispatch_table_for_class (next); + objc_mutex_unlock(__objc_runtime_mutex); } *************** *** 330,334 **** /* This function adds a method list to a class. This function is typically called by another function specific to the run-time. As ! such this function does not worry about thread safe issued. This one is only called for categories. Class objects have their --- 346,350 ---- /* This function adds a method list to a class. This function is typically called by another function specific to the run-time. As ! such this function does not worry about thread safe issues. This one is only called for categories. Class objects have their *************** *** 339,343 **** { int i; ! static SEL initialize_sel = 0; if (!initialize_sel) initialize_sel = sel_register_name ("initialize"); --- 355,360 ---- { int i; ! static SEL initialize_sel = 0; /* !T:SAFE2 */ ! if (!initialize_sel) initialize_sel = sel_register_name ("initialize"); *************** *** 483,487 **** { IMP imp; ! static SEL frwd_sel = 0; SEL err_sel; --- 500,504 ---- { IMP imp; ! static SEL frwd_sel = 0; /* !T:SAFE2 */ SEL err_sel; *************** *** 535,538 **** --- 552,558 ---- { int total = 0; + + objc_mutex_lock(__objc_runtime_mutex); + printf("memory usage: (%s)\n", #ifdef OBJC_SPARSE2 *************** *** 553,556 **** --- 573,578 ---- printf("total: %d bytes\n", total); printf("===================================\n"); + + objc_mutex_unlock(__objc_runtime_mutex); } diff -rc2P objc/thread-decosf1.c objc-threaded/thread-decosf1.c *** objc/thread-decosf1.c Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-decosf1.c Sun Jan 21 19:36:27 1996 *************** *** 0 **** --- 1,325 ---- + /* GNU Objective C Runtime Thread Interface + Copyright (C) 1995 Free Software Foundation, Inc. + + Author: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is included into thread.c + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License along with + GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + #include + + /******** + * This structure represents a single mutual exclusion lock. Lock semantics + * are detailed with the subsequent functions. We use whatever lock is + * provided by the system. We augment it with depth and current owner id + * fields to implement and re-entrant lock. + */ + struct _objc_mutex + { + volatile _objc_thread_t owner; /* Id of thread that owns. */ + volatile int depth; /* # of acquires. */ + pthread_mutex_t lock; /* pthread mutex. */ + }; + + /***************************************************************************** + * Static variables. + */ + static pthread_key_t __objc_thread_data_key; /* Data key for thread data.*/ + + + /******** + * Initialize the threads subsystem. Returns 0 if successful, or -1 if no + * thread support is available. + */ + int + __objc_init_thread_system(void) + { + printf("__objc_init_thread_system\n"); + + if (pthread_keycreate(&__objc_thread_data_key, NULL) == 0) + return 0; /* Yes, return success. */ + + return -1; /* Failed. */ + } + + int + __objc_fini_thread_system(void) + { + return 0; + } + + /******** + * Create a new thread of execution and return its id. Return NULL if fails. + * The new thread starts in "func" with the given argument. + */ + _objc_thread_t + objc_thread_create(void (*func)(void *arg), void *arg) + { + _objc_thread_t thread_id = NULL; /* Detached thread id. */ + pthread_t new_thread_handle; /* DCE thread handle. */ + + objc_mutex_lock(__objc_runtime_mutex); + + if (pthread_create(&new_thread_handle, pthread_attr_default, + (void *)func, arg) == 0) { + thread_id = *(_objc_thread_t *)&new_thread_handle; /* ??? May not work! (64bit)*/ + pthread_detach(&new_thread_handle); /* Fully detach thread. */ + __objc_runtime_threads_alive++; + } + + objc_mutex_unlock(__objc_runtime_mutex); + return thread_id; + } + + /******** + * Set the current thread's priority. + */ + int + objc_thread_set_priority(int priority) + { + int sys_priority = 0; + + switch (priority) { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = (PRI_FG_MIN_NP + PRI_FG_MAX_NP) / 2; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2; + break; + } + + if (pthread_setprio(pthread_self(), sys_priority) >= 0) + return 0; /* Changed priority. End. */ + + return -1; /* Failed. */ + } + + /******** + * Return the current thread's priority. + */ + int + objc_thread_get_priority(void) + { + int sys_priority; /* DCE thread priority. */ + + if ((sys_priority = pthread_getprio(pthread_self())) >= 0) { + if (sys_priority >= PRI_FG_MIN_NP && sys_priority <= PRI_FG_MAX_NP) + return OBJC_THREAD_INTERACTIVE_PRIORITY; + if (sys_priority >= PRI_BG_MIN_NP && sys_priority <= PRI_BG_MAX_NP) + return OBJC_THREAD_BACKGROUND_PRIORITY; + return OBJC_THREAD_LOW_PRIORITY; + } + return -1; /* Couldn't get priority. */ + } + + /******** + * Yield our process time to another thread. Any BUSY waiting that is done + * by a thread should use this function to make sure that other threads can + * make progress even on a lazy uniprocessor system. + */ + void + objc_thread_yield(void) + { + pthread_yield(); /* Yield to equal thread. */ + } + + /******** + * Terminate the current tread. Doesn't return anything. Doesn't return. + * Actually, if it failed returns -1. + */ + int + objc_thread_exit(void) + { + objc_mutex_lock(__objc_runtime_mutex); + __objc_runtime_threads_alive--; + objc_mutex_unlock(__objc_runtime_mutex); + + pthread_exit(&__objc_thread_exit_status); /* Terminate thread. */ + return -1; + } + + /******** + * Returns an integer value which uniquely describes a thread. Must not be + * -1 which is reserved as a marker for "no thread". + */ + int + objc_thread_id(void) + { + pthread_t self = pthread_self(); + + return *(int *)&self; /* Return thread handle. */ + } + + /******** + * Sets the thread's local storage pointer. Returns 0 if successful or -1 + * if failed. + */ + int + objc_thread_set_data(void *value) + { + if (pthread_setspecific(__objc_thread_data_key, (void *)value) == 0) + return 0; /* Return thread data. */ + return -1; + } + + /******** + * Returns the thread's local storage pointer. Returns NULL on failure. + */ + void * + objc_thread_get_data(void) + { + void * value = NULL; + + if (pthread_getspecific(__objc_thread_data_key, (void *)&value) == 0) + return value; /* Return thread data. */ + + return NULL; + } + + /******** + * Allocate a mutex. Return the mutex pointer if successful or NULL if + * the allocation fails for any reason. + */ + _objc_mutex_t + objc_mutex_allocate(void) + { + _objc_mutex_t mutex; + int err = 0; + + if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex)))) + return NULL; /* Abort if malloc failed. */ + + err = pthread_mutex_init(&mutex->lock, pthread_mutexattr_default); + + if (err != 0) { /* System init failed? */ + free(mutex); /* Yes, free local memory. */ + return NULL; /* Abort. */ + } + mutex->owner = -1; /* No owner. */ + mutex->depth = 0; /* No locks. */ + return mutex; /* Return mutex handle. */ + } + + /******** + * 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 deallotcate 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; /* # of locks on mutex. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + depth = objc_mutex_lock(mutex); /* Must have lock. */ + + pthread_mutex_unlock(&mutex->lock); /* Must unlock system mutex.*/ + pthread_mutex_destroy(&mutex->lock); /* Free system mutex. */ + + free(mutex); /* Free memory. */ + return depth; /* Return last depth. */ + } + + /******** + * Grab a lock on a mutex. If this thread already has a lock on this mutex + * then we increment the lock count. If another thread has a lock on the + * mutex we block and wait for the thread to release the lock. + * Returns the lock count on the mutex held by this thread. + */ + int + objc_mutex_lock(_objc_mutex_t mutex) + { + int thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + if (pthread_mutex_lock(&mutex->lock) != 0) /* Lock DCE system mutex. */ + return -1; /* Failed, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Try to grab a lock on a mutex. If this thread already has a lock on + * this mutex then we increment the lock count and return it. If another + * thread has a lock on the mutex returns -1. + */ + int + objc_mutex_trylock(_objc_mutex_t mutex) + { + int thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + if (pthread_mutex_trylock(&mutex->lock) != 1) /* Lock DCE system mutex. */ + return -1; /* Failed, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Decrements the lock count on this mutex by one. If the lock count reaches + * zero, release the lock on the mutex. Returns the lock count on the mutex. + * It is an error to attempt to unlock a mutex which this thread doesn't hold + * in which case return -1 and the mutex is unaffected. + * Will also return -1 if the mutex free fails. + */ + int + objc_mutex_unlock(_objc_mutex_t mutex) + { + int thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner != thread_id) /* Does some else own lock? */ + return -1; /* Yes, abort. */ + if (mutex->depth > 1) /* Released last lock? */ + return --mutex->depth; /* No, Decrement depth, end.*/ + mutex->depth = 0; /* Yes, reset depth to 0. */ + mutex->owner = -1; /* Set owner to "no thread".*/ + + if (pthread_mutex_unlock(&mutex->lock) != 0) /* Unlock system mutex. */ + return -1; /* Failed, abort. */ + + return 0; /* No, return success. */ + } + + /* End of File */ diff -rc2P objc/thread-irix.c objc-threaded/thread-irix.c *** objc/thread-irix.c Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-irix.c Sun Jan 21 19:36:27 1996 *************** *** 0 **** --- 1,314 ---- + /* GNU Objective C Runtime Thread Interface - SGI IRIX Implementation + Copyright (C) 1995 Free Software Foundation, Inc. + + Author: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is included into thread.c + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License along with + GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + #include + #include + #include + #include + #include + + /******** + * This structure represents a single mutual exclusion lock. Lock semantics + * are detailed with the subsequent functions. We use whatever lock is + * provided by the system. We augment it with depth and current owner id + * fields to implement and re-entrant lock. + */ + struct _objc_mutex + { + volatile _objc_thread_t owner; /* Id of thread that owns. */ + volatile int depth; /* # of acquires. */ + ulock_t lock; /* Irix lock. */ + }; + + /***************************************************************************** + * Static variables. + */ + static void * __objc_shared_arena_handle = NULL; /* Storage arena locks. */ + + /******** + * Initialize the threads subsystem. Returns 0 if successful, or -1 if no + * thread support is available. + */ + int + __objc_init_thread_system(void) + { + char arena_name[64]; /* Name of IRIX arena. */ + + DEBUG_PRINTF("__objc_init_thread_system\n"); + sprintf(arena_name, "/usr/tmp/objc_%05u", (unsigned)getpid()); + usconfig(CONF_INITUSERS, 256); /* Up to 256 threads. */ + usconfig(CONF_ARENATYPE, US_SHAREDONLY); /* Arena only for threads. */ + if (!(__objc_shared_arena_handle = usinit(arena_name))) /* Init Failed? */ + return -1; /* Yes, return error code. */ + + return 0; + } + + int + __objc_fini_thread_system(void) + { + return 0; + } + + /******** + * Create a new thread of execution and return its id. Return NULL if fails. + * The new thread starts in "func" with the given argument. + */ + _objc_thread_t + objc_thread_create(void (*func)(void *arg), void *arg) + { + _objc_thread_t thread_id = NULL; + int sys_id; + + objc_mutex_lock(__objc_runtime_mutex); + if ((sys_id = sproc((void *)func, PR_SALL, arg)) >= 0) { + thread_id = (_objc_thread_t)sys_id; + __objc_runtime_threads_alive++; + } + objc_mutex_unlock(__objc_runtime_mutex); + + return thread_id; + } + + /******** + * Set the current thread's priority. + */ + int + objc_thread_set_priority(int priority) + { + int sys_priority = 0; + + switch (priority) { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + break; + case OBJC_THREAD_LOW_PRIORITY: + break; + } + return -1; /* Failed. */ + } + + /******** + * Return the current thread's priority. + */ + int + objc_thread_get_priority(void) + { + return -1; /* Couldn't get priority. */ + } + + /******** + * Yield our process time to another thread. Any BUSY waiting that is done + * by a thread should use this function to make sure that other threads can + * make progress even on a lazy uniprocessor system. + */ + void + objc_thread_yield(void) + { + sginap(0); /* Yield to equal process. */ + } + + /******** + * Terminate the current tread. Doesn't return anything. Doesn't return. + * Actually, if it failed returns -1. + */ + int + objc_thread_exit(void) + { + objc_mutex_lock(__objc_runtime_mutex); + __objc_runtime_threads_alive--; + objc_mutex_unlock(__objc_runtime_mutex); + + exit(__objc_thread_exit_status); /* IRIX only has exit. */ + return -1; + } + + /******** + * Returns an integer value which uniquely describes a thread. Must not be + * NULL which is reserved as a marker for "no thread". + */ + _objc_thread_t + objc_thread_id(void) + { + return (_objc_thread_t)get_pid(); /* Threads are processes. */ + } + + /******** + * Sets the thread's local storage pointer. Returns 0 if successful or -1 + * if failed. + */ + int + objc_thread_set_data(void *value) + { + *((void **)&PRDA->usr_prda) = value; /* Set thread data ptr. */ + return 0; + } + + /******** + * Returns the thread's local storage pointer. Returns NULL on failure. + */ + void * + objc_thread_get_data(void) + { + return *((void **)&PRDA->usr_prda); /* Return thread data ptr. */ + } + + /******** + * Allocate a mutex. + * Return the mutex pointer if successful or NULL if the allocation failed + * for any reason. + */ + _objc_mutex_t + objc_mutex_allocate(void) + { + _objc_mutex_t mutex; + int err = 0; + + if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex)))) + return NULL; /* Abort if malloc failed. */ + + if (!(mutex->lock = usnewlock(__objc_shared_arena_handle))) + err = -1; + + if (err != 0) { /* System init failed? */ + free(mutex); /* Yes, free local memory. */ + return NULL; /* Abort. */ + } + mutex->owner = NULL; /* No owner. */ + mutex->depth = 0; /* No locks. */ + return mutex; /* Return mutex handle. */ + } + + /******** + * 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 deallotcate 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; /* # of locks on mutex. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + depth = objc_mutex_lock(mutex); /* Must have lock. */ + + usfreelock(mutex->lock, __objc_shared_arena_handle); /* Free IRIX lock. */ + + free(mutex); /* Free memory. */ + return depth; /* Return last depth. */ + } + + /******** + * Grab a lock on a mutex. If this thread already has a lock on this mutex + * then we increment the lock count. If another thread has a lock on the + * mutex we block and wait for the thread to release the lock. + * Returns the lock count on the mutex held by this thread. + */ + int + objc_mutex_lock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) { /* Already own lock? */ + DEBUG_PRINTF("lock owned by: %d:%d\n", mutex->owner, mutex->depth); + return ++mutex->depth; /* Yes, increment depth. */ + } + + DEBUG_PRINTF("lock owned by: %d:%d (attempt by %d)\n", + mutex->owner, mutex->depth, thread_id); + + if (ussetlock(mutex->lock) == 0) /* Did lock acquire fail? */ + return -1; /* Yes, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Try to grab a lock on a mutex. If this thread already has a lock on + * this mutex then we increment the lock count and return it. If another + * thread has a lock on the mutex returns -1. + */ + int + objc_mutex_trylock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + if (ustestlock(mutex->lock) == 0) /* Did lock acquire fail? */ + return -1; /* Yes, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Decrements the lock count on this mutex by one. If the lock count reaches + * zero, release the lock on the mutex. Returns the lock count on the mutex. + * It is an error to attempt to unlock a mutex which this thread doesn't hold + * in which case return -1 and the mutex is unaffected. + * Will also return -1 if the mutex free fails. + */ + + int + objc_mutex_unlock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner != thread_id) /* Does some else own lock? */ + return -1; /* Yes, abort. */ + + DEBUG_PRINTF("unlock by: %d:%d\n", mutex->owner, mutex->depth - 1); + + if (mutex->depth > 1) /* Released last lock? */ + return --mutex->depth; /* No, Decrement depth, end.*/ + mutex->depth = 0; /* Yes, reset depth to 0. */ + mutex->owner = NULL; /* Set owner to "no thread".*/ + + usunsetlock(mutex->lock); /* Free lock. */ + + return 0; /* No, return success. */ + } + + /* End of File */ diff -rc2P objc/thread-single.c objc-threaded/thread-single.c *** objc/thread-single.c Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-single.c Sun Jan 21 19:36:27 1996 *************** *** 0 **** --- 1,239 ---- + /* GNU Objective C Runtime Thread Implementation + Copyright (C) 1995 Free Software Foundation, Inc. + + Author: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is included into thread.c + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License along with + GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + /******** + * This structure represents a single mutual exclusion lock. Lock semantics + * are detailed with the subsequent functions. We use whatever lock is + * provided by the system. We augment it with depth and current owner id + * fields to implement and re-entrant lock. + */ + struct _objc_mutex + { + volatile _objc_thread_t owner; /* Id of thread that owns. */ + volatile int depth; /* # of acquires. */ + }; + + /******** + * Initialize the threads subsystem. Returns 0 if successful, or -1 if no + * thread support is available. + */ + int + __objc_init_thread_system(void) + { + DEBUG_PRINTF("__objc_init_thread_system\n"); + return -1; /* Failed. */ + } + + /******** + * Create a new thread of execution and return its id. Return NULL if fails. + * The new thread starts in "func" with the given argument. + */ + _objc_thread_t + objc_thread_create(void (*func)(void *arg), void *arg) + { + return NULL; /* We can't start threads. */ + } + + /******** + * Set the current thread's priority. + */ + int + objc_thread_set_priority(int priority) + { + return -1; /* Failed. */ + } + + /******** + * Return the current thread's priority. + */ + int + objc_thread_get_priority(void) + { + return OBJC_THREAD_INTERACTIVE_PRIORITY; /* Highest priority. */ + } + + /******** + * Yield our process time to another thread. Any BUSY waiting that is done + * by a thread should use this function to make sure that other threads can + * make progress even on a lazy uniprocessor system. + */ + void + objc_thread_yield(void) + { + return; + } + + /******** + * Terminate the current tread. Doesn't return anything. Doesn't return. + * Actually, if it failed returns -1. + */ + int + objc_thread_exit(void) + { + exit(__objc_thread_exit_status); + return -1; + } + + /******** + * Returns an integer value which uniquely describes a thread. Must not be + * NULL which is reserved as a marker for "no thread". + */ + _objc_thread_t + objc_thread_id(void) + { + return (_objc_thread_t)1; /* No thread support, use 1.*/ + } + + /******** + * Sets the thread's local storage pointer. Returns 0 if successful or -1 + * if failed. + */ + + static void *thread_local_storage = NULL; + + int + objc_thread_set_data(void *value) + { + thread_local_storage = value; + return 0; + } + + /******** + * Returns the thread's local storage pointer. Returns NULL on failure. + */ + void * + objc_thread_get_data(void) + { + return thread_local_storage; + } + + /******** + * Allocate a mutex. Return the mutex pointer if successful or NULL if the + * allocation failed for any reason. + */ + _objc_mutex_t + objc_mutex_allocate(void) + { + _objc_mutex_t mutex; + + if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex)))) + return NULL; /* Abort if malloc failed. */ + + mutex->owner = NULL; /* No owner. */ + mutex->depth = 0; /* No locks. */ + return mutex; /* Return mutex handle. */ + } + + /******** + * 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; /* # of locks on mutex. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + depth = objc_mutex_lock(mutex); /* Must have lock. */ + + free(mutex); /* Free memory. */ + return depth; /* Return last depth. */ + } + + /******** + * Grab a lock on a mutex. If this thread already has a lock on this mutex + * then we increment the lock count. If another thread has a lock on the + * mutex we block and wait for the thread to release the lock. + * Returns the lock count on the mutex held by this thread. + */ + int + objc_mutex_lock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Try to grab a lock on a mutex. If this thread already has a lock on + * this mutex then we increment the lock count and return it. If another + * thread has a lock on the mutex returns -1. + */ + int + objc_mutex_trylock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Decrements the lock count on this mutex by one. If the lock count reaches + * zero, release the lock on the mutex. Returns the lock count on the mutex. + * It is an error to attempt to unlock a mutex which this thread doesn't hold + * in which case return -1 and the mutex is unaffected. + * Will also return -1 if the mutex free fails. + */ + int + objc_mutex_unlock(_objc_mutex_t mutex) + { + int thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner != thread_id) /* Does some else own lock? */ + return -1; /* Yes, abort. */ + if (mutex->depth > 1) /* Released last lock? */ + return --mutex->depth; /* No, Decrement depth, end.*/ + mutex->depth = 0; /* Yes, reset depth to 0. */ + mutex->owner = NULL; /* Set owner to "no thread".*/ + + return 0; /* No, return success. */ + } + + /* End of File */ diff -rc2P objc/thread-solaris.c objc-threaded/thread-solaris.c *** objc/thread-solaris.c Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-solaris.c Sun Jan 21 19:36:27 1996 *************** *** 0 **** --- 1,328 ---- + /* GNU Objective C Runtime Thread Interface + Copyright (C) 1995 Free Software Foundation, Inc. + + Author: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is included into thread.c + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License along with + GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + #include "runtime.h" + + #include + #include + #include + + /******** + * This structure represents a single mutual exclusion lock. Lock semantics + * are detailed with the subsequent functions. We use whatever lock is + * provided by the system. We augment it with depth and current owner id + * fields to implement and re-entrant lock. + */ + struct _objc_mutex + { + volatile _objc_thread_t owner; /* Id of thread that owns. */ + volatile int depth; /* # of acquires. */ + mutex_t lock; /* System mutex. */ + }; + + /***************************************************************************** + * Static variables. + */ + static thread_key_t __objc_thread_data_key; /* Data key for thread data.*/ + + /******** + * Initialize the threads subsystem. Returns 0 if successful, or -1 if no + * thread support is available. + */ + int + __objc_init_thread_system(void) + { + DEBUG_PRINTF("__objc_init_thread_system\n"); + + if (thr_keycreate(&__objc_thread_data_key, NULL) == 0) + return 0; /* Yes, return success. */ + + return -1; /* Failed. */ + } + + int + __objc_fini_thread_system(void) + { + return 0; + } + + /******** + * Create a new thread of execution and return its id. Return -1 if fails. + * The new thread starts in "func" with the given argument. + */ + _objc_thread_t + objc_thread_create(void (*func)(void *arg), void *arg) + { + _objc_thread_t thread_id = NULL; /* Detached thread id. */ + thread_t new_thread_id = 0; /* Solaris thread id type. */ + int errn; + + objc_mutex_lock(__objc_runtime_mutex); + + if (thr_create(NULL, 0, (void *)func, arg, + THR_DETACHED | THR_NEW_LWP, + &new_thread_id) == 0) { /* Created new thread? */ + thread_id = (_objc_thread_t)new_thread_id; /* Yes, remember its id. */ + __objc_runtime_threads_alive++; + } + + objc_mutex_unlock(__objc_runtime_mutex); + + return thread_id; + } + + /******** + * Set the current thread's priority. + */ + int + objc_thread_set_priority(int priority) + { + int sys_priority = 0; + + switch (priority) { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = 300; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = 200; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = 1000; + break; + } + + if (thr_setprio(thr_self(), sys_priority) == 0) + return 0; /* Changed priority. End. */ + + return -1; /* Failed. */ + } + + /******** + * Return the current thread's priority. + */ + int + objc_thread_get_priority(void) + { + int sys_priority; /* Solaris thread priority. */ + + if (thr_getprio(thr_self(), &sys_priority) == 0) { + if (sys_priority >= 250) + return OBJC_THREAD_INTERACTIVE_PRIORITY; + else if (sys_priority >= 150) + return OBJC_THREAD_BACKGROUND_PRIORITY; + return OBJC_THREAD_LOW_PRIORITY; + } + + return -1; /* Couldn't get priority. */ + } + + /******** + * Yield our process time to another thread. Any BUSY waiting that is done + * by a thread should use this function to make sure that other threads can + * make progress even on a lazy uniprocessor system. + */ + void + objc_thread_yield(void) + { + thr_yield(); /* Yield to equal thread. */ + } + + /******** + * Terminate the current tread. Doesn't return anything. Doesn't return. + * Actually, if it failed returns -1. + */ + int + objc_thread_exit(void) + { + objc_mutex_lock(__objc_runtime_mutex); + __objc_runtime_threads_alive++; + objc_mutex_unlock(__objc_runtime_mutex); + + thr_exit(&__objc_thread_exit_status); /* Terminate thread. */ + return -1; + } + + /******** + * Returns an integer value which uniquely describes a thread. Must not be + * NULL which is reserved as a marker for "no thread". + */ + _objc_thread_t + objc_thread_id(void) + { + return (_objc_thread_t)thr_self(); + } + + /******** + * Sets the thread's local storage pointer. Returns 0 if successful or -1 + * if failed. + */ + int + objc_thread_set_data(void *value) + { + if (thr_setspecific(__objc_thread_data_key, value) == 0) + return 0; + return -1; + } + + /******** + * Returns the thread's local storage pointer. Returns NULL on failure. + */ + void * + objc_thread_get_data(void) + { + void * value = NULL; + + if (thr_getspecific(__objc_thread_data_key, &value) == 0) + return value; /* Return thread data. */ + + return NULL; + } + + /******** + * Allocate a mutex. Return the mutex pointer if successful or NULL if + * the allocation fails for any reason. + */ + _objc_mutex_t + objc_mutex_allocate(void) + { + struct _objc_mutex *mutex; + int err = 0; + + if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex)))) + return NULL; /* Abort if malloc failed. */ + + err = mutex_init(&mutex->lock, USYNC_THREAD, 0); + + if (err != 0) { /* System init failed? */ + free(mutex); /* Yes, free local memory. */ + return NULL; /* Abort. */ + } + mutex->owner = NULL; /* No owner. */ + mutex->depth = 0; /* No locks. */ + return mutex; /* Return mutex handle. */ + } + + /******** + * 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 deallotcate 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; /* # of locks on mutex. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + depth = objc_mutex_lock(mutex); /* Must have lock. */ + + mutex_destroy(&mutex->lock); /* System deallocate. */ + + free(mutex); /* Free memory. */ + return depth; /* Return last depth. */ + } + + /******** + * Grab a lock on a mutex. If this thread already has a lock on this mutex + * then we increment the lock count. If another thread has a lock on the + * mutex we block and wait for the thread to release the lock. + * Returns the lock count on the mutex held by this thread. + */ + int + objc_mutex_lock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + if (mutex_lock(&mutex->lock) != 0) /* Did lock acquire fail? */ + return -1; /* Yes, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Try to grab a lock on a mutex. If this thread already has a lock on + * this mutex then we increment the lock count and return it. If another + * thread has a lock on the mutex returns -1. + */ + int + objc_mutex_trylock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + if (mutex_trylock(&mutex->lock) != 0) /* Did lock acquire fail? */ + return -1; /* Yes, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return mutex->depth = 1; /* Increment depth to end. */ + } + + /******** + * Decrements the lock count on this mutex by one. If the lock count reaches + * zero, release the lock on the mutex. Returns the lock count on the mutex. + * It is an error to attempt to unlock a mutex which this thread doesn't hold + * in which case return -1 and the mutex is unaffected. + * Will also return -1 if the mutex free fails. + */ + int + objc_mutex_unlock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner != thread_id) /* Does some else own lock? */ + return -1; /* Yes, abort. */ + if (mutex->depth > 1) /* Released last lock? */ + return --mutex->depth; /* No, Decrement depth, end.*/ + mutex->depth = 0; /* Yes, reset depth to 0. */ + mutex->owner = NULL; /* Set owner to "no thread".*/ + + if (mutex_unlock(&mutex->lock) != 0) /* Did lock release fail? */ + return -1; /* Yes, return error value. */ + + return 0; /* No, return success. */ + } + + /* End of File */ diff -rc2P objc/thread-test/Makefile objc-threaded/thread-test/Makefile *** objc/thread-test/Makefile Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-test/Makefile Sun Jan 21 20:06:00 1996 *************** *** 0 **** --- 1,45 ---- + ############################################################################## + # + # + # + + all: test1 + + ######## + # Solaris: + # + # test1 : test1.o ../libobjc-thread.a + # gcc -g -o test1 test1.o ../libobjc-thread.a -lthread + + ######## + # Linux: + # + # test1 : test1.o ../libobjc-thread.a + # gcc -g -o test1 test1.o ../libobjc-thread.a -lieee + + ######## + # Others: + # + test1 : test1.o ../libobjc-thread.a + gcc -g -o test1 test1.o ../libobjc-thread.a + + test1.o : test1.m + gcc -DOBJC_THREAD_SAFE -g -I../.. -o test1.o -c test1.m + + ######## + + clean: + rm -f *.o test1 + + test: test1 + time test1 + # + + + + + + + + + diff -rc2P objc/thread-test/test1.m objc-threaded/thread-test/test1.m *** objc/thread-test/test1.m Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-test/test1.m Sun Jan 21 19:46:28 1996 *************** *** 0 **** --- 1,160 ---- + // + + #include + #include + #include + #include + + ////////////////////////////////////////////////////////////////////////////// + + @interface NSLocking : Object + { + _objc_mutex_t mutex; + } + + - init; + - free; + - (void)lock; + - (void)unlock; + + @end + + @implementation NSLocking + + - init + { + [super init]; + + mutex = objc_mutex_allocate(); + return self; + } + + - free + { + objc_mutex_deallocate(mutex); + return [super free]; + } + + - (void)lock + { + objc_mutex_lock(mutex); + } + + - (void)unlock + { + objc_mutex_lock(mutex); + } + + @end + + ////////////////////////////////////////////////////////////////////////////// + + @interface Fruit : Object + { + int m_refs; + const char *m_name; + volatile int m_done; + } + + /* Obtaining attributes intrinsic to the protocol */ + + - init; + - free; + - (const char *)name; + - set_name: (const char *)name; + - loop: (void *)arg; + - (int)bogus: (int)n; + - wait; + + + @end + + @implementation Fruit + + - init + { + [super init]; + m_refs = 1; + m_name = ""; + m_done = 0; + return self; + } + + - free + { + printf("[%s free, refs: %d]\n", m_name, m_refs); + return [super free]; + } + + - (const char *)name + { + return m_name; + } + + - set_name: (const char *)name + { + m_name = name; + return self; + } + + - loop: (void *)arg + { + int i; + + printf("%s loop start\n", m_name); + fflush(stdout); + for (i = 0; i < 100000;) { + i = [self bogus: i]; + if (i % 10000 == 1) + printf("%s loop: %d\n", m_name, i); + } + printf("%s loop done\n", m_name); + m_done = 1; + + return self; + } + + - (int)bogus: (int)n + { + return n + 1; + } + + - wait + { + while (!m_done) + ; + + return self; + } + + @end + + + int main(int argc, char **argv) + { + id f1, f2, f3; + id (*imp)(id,SEL,id); + + printf("Main:\n"); + + f1 = [[Fruit new] set_name: "f1"]; + f2 = [[Fruit new] set_name: "f2"]; + f3 = [[Fruit new] set_name: "f3"]; + + printf("Inside the main body.\n"); + objc_thread_detach(@selector(loop:), f1, NULL); + objc_thread_detach(@selector(loop:), f2, NULL); + + [f3 loop: NULL]; + + [f1 wait]; + [f2 wait]; + + [f1 free]; + [f2 free]; + [f3 free]; + + printf("Done:\n"); + + return 0; + } diff -rc2P objc/thread-win32.c objc-threaded/thread-win32.c *** objc/thread-win32.c Wed Dec 31 19:00:00 1969 --- objc-threaded/thread-win32.c Mon Jan 22 11:55:50 1996 *************** *** 0 **** --- 1,333 ---- + /* GNU Objective C Runtime Thread Interface - Win32 Implementation + Copyright (C) 1995 Free Software Foundation, Inc. + + Author: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is included into thread.c + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License along with + GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + #include + + /******** + * This structure represents a single mutual exclusion lock. Lock semantics + * are detailed with the subsequent functions. We use whatever lock is + * provided by the system. We augment it with depth and current owner id + * fields to implement and re-entrant lock. + */ + struct _objc_mutex + { + volatile _objc_thread_t owner; /* Id of thread that owns. */ + volatile int depth; /* # of acquires. */ + HANDLE handle; /* Win32 mutex HANDLE. */ + }; + + /***************************************************************************** + * Static variables. + */ + static DWORD __objc_data_tls = (DWORD)-1; /* Win32 Thread Local Index.*/ + + /******** + * Initialize the threads subsystem. Returns 0 if successful, or -1 if no + * thread support is available. + */ + int + __objc_init_thread_system(void) + { + DEBUG_PRINTF("__objc_init_thread_system\n"); + + if ((__objc_data_tls = TlsAlloc()) != (DWORD)-1) + return 0; /* Yes, return success. */ + + return -1; /* Failed. */ + } + + int + __objc_fini_thread_system(void) + { + if (__objc_data_tls != (DWORD)-1) { + TlsFree(__objc_data_tls); + return 0; + } + return -1; + } + + /******** + * Create a new thread of execution and return its id. Return NULL if fails. + * The new thread starts in "func" with the given argument. + */ + _objc_thread_t + objc_thread_create(void (*func)(void *arg), void *arg) + { + DWORD thread_id = 0; /* Detached thread id. */ + HANDLE win32_handle; /* Win32 thread handle. */ + + objc_mutex_lock(__objc_runtime_mutex); + + if ((win32_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, + arg, 0, &thread_id))) { + __objc_runtime_threads_alive++; + } + else + thread_id = 0; + + objc_mutex_unlock(__objc_runtime_mutex); + + return (_objc_thread_t)thread_id; + } + + /******** + * Set the current thread's priority. + */ + int + objc_thread_set_priority(int priority) + { + int sys_priority = 0; + + switch (priority) { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = THREAD_PRIORITY_NORMAL; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = THREAD_PRIORITY_LOWEST; + break; + } + if (SetThreadPriority(GetCurrentThread(), sys_priority)) + return 0; /* Changed priority. End. */ + + return -1; /* Failed. */ + } + + /******** + * Return the current thread's priority. + */ + int + objc_thread_get_priority(void) + { + int sys_priority; + + sys_priority = GetThreadPriority(GetCurrentThread()); + + switch (sys_priority) { + case THREAD_PRIORITY_HIGHEST: + case THREAD_PRIORITY_TIME_CRITICAL: + case THREAD_PRIORITY_ABOVE_NORMAL: + case THREAD_PRIORITY_NORMAL: + return OBJC_THREAD_INTERACTIVE_PRIORITY; + + default: + case THREAD_PRIORITY_BELOW_NORMAL: + return OBJC_THREAD_BACKGROUND_PRIORITY; + + case THREAD_PRIORITY_IDLE: + case THREAD_PRIORITY_LOWEST: + return OBJC_THREAD_LOW_PRIORITY; + } + return -1; /* Couldn't get priority. */ + } + + /******** + * Yield our process time to another thread. Any BUSY waiting that is done + * by a thread should use this function to make sure that other threads can + * make progress even on a lazy uniprocessor system. + */ + void + objc_thread_yield(void) + { + Sleep(0); /* Yield to equal thread. */ + } + + /******** + * Terminate the current tread. Doesn't return anything. Doesn't return. + * Actually, if it failed returns -1. + */ + int + objc_thread_exit(void) + { + objc_mutex_lock(__objc_runtime_mutex); + __objc_runtime_threads_alive--; + objc_mutex_unlock(__objc_runtime_mutex); + + ExitThread(__objc_thread_exit_status); /* Terminate thread. */ + return -1; + } + + /******** + * Returns an integer value which uniquely describes a thread. Must not be + * -1 which is reserved as a marker for "no thread". + */ + _objc_thread_t + objc_thread_id(void) + { + return (_objc_thread_t)GetCurrentThreadId(); /* Return thread id. */ + } + + /******** + * Sets the thread's local storage pointer. Returns 0 if successful or -1 + * if failed. + */ + int + objc_thread_set_data(void *value) + { + if (TlsSetValue(__objc_data_tls, value)) + return 0; /* Return thread data. */ + return -1; + } + + /******** + * Returns the thread's local storage pointer. Returns NULL on failure. + */ + void * + objc_thread_get_data(void) + { + return TlsGetValue(__objc_data_tls); /* Return thread data. */ + } + + /******** + * Allocate a mutex. Return the mutex pointer if successful or NULL if + * the allocation fails for any reason. + */ + _objc_mutex_t + objc_mutex_allocate(void) + { + _objc_mutex_t mutex; + int err = 0; + + if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex)))) + return NULL; /* Abort if malloc failed. */ + + if ((mutex->handle = CreateMutex(NULL, 0, NULL)) == NULL) { + free(mutex); /* Failed, free memory. */ + return NULL; /* Abort. */ + } + mutex->owner = NULL; /* No owner. */ + mutex->depth = 0; /* No locks. */ + return mutex; /* Return mutex handle. */ + } + + /******** + * 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 deallotcate 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; /* # of locks on mutex. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + depth = objc_mutex_lock(mutex); /* Must have lock. */ + + CloseHandle(mutex->handle); /* Close Win32 handle. */ + + free(mutex); /* Free memory. */ + return depth; /* Return last depth. */ + } + + /******** + * Grab a lock on a mutex. If this thread already has a lock on this mutex + * then we increment the lock count. If another thread has a lock on the + * mutex we block and wait for the thread to release the lock. + * Returns the lock count on the mutex held by this thread. + */ + int + objc_mutex_lock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + int status; + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + status = WaitForSingleObject(mutex->handle, INFINITE); + if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED) + return -1; /* Failed, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + + return ++mutex->depth; /* Increment depth to end. */ + } + + /******** + * Try to grab a lock on a mutex. If this thread already has a lock on + * this mutex then we increment the lock count and return it. If another + * thread has a lock on the mutex returns -1. + */ + int + objc_mutex_trylock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + DWORD status; /* Return status from Win32.*/ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner == thread_id) /* Already own lock? */ + return ++mutex->depth; /* Yes, increment depth. */ + + status = WaitForSingleObject(mutex->handle, 0); + if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED) + return -1; /* Failed, abort. */ + + mutex->owner = thread_id; /* Mark thread as owner. */ + return ++mutex->depth; /* Increment depth to end. */ + } + + /******** + * Decrements the lock count on this mutex by one. If the lock count reaches + * zero, release the lock on the mutex. Returns the lock count on the mutex. + * It is an error to attempt to unlock a mutex which this thread doesn't hold + * in which case return -1 and the mutex is unaffected. + * Will also return -1 if the mutex free fails. + */ + int + objc_mutex_unlock(_objc_mutex_t mutex) + { + _objc_thread_t thread_id; /* Cache our thread id. */ + + if (!mutex) /* Is argument bad? */ + return -1; /* Yes, abort. */ + thread_id = objc_thread_id(); /* Get this thread's id. */ + if (mutex->owner != thread_id) /* Does some else own lock? */ + return -1; /* Yes, abort. */ + if (mutex->depth > 1) /* Released last lock? */ + return --mutex->depth; /* No, Decrement depth, end.*/ + mutex->depth = 0; /* Yes, reset depth to 0. */ + mutex->owner = NULL; /* Set owner to "no thread".*/ + + if (ReleaseMutex(mutex->handle) == 0) + return -1; /* Failed, abort. */ + + return 0; /* No, return success. */ + } + + /* End of File */ diff -rc2P objc/thread.c objc-threaded/thread.c *** objc/thread.c Wed Dec 31 19:00:00 1969 --- objc-threaded/thread.c Fri Jan 26 14:40:09 1996 *************** *** 0 **** --- 1,132 ---- + /* GNU Objective C Runtime Thread Interface + Copyright (C) 1995 Free Software Foundation, Inc. + + Author: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License along with + GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + + #include + #include "runtime.h" + + /***************************************************************************** + * Universal static variables: + */ + int __objc_thread_exit_status = 0; /* Global exit status. */ + + /***************************************************************************** + * Universal Functionality + */ + + /******** + * First function called in a thread, starts everything else. + */ + struct __objc_thread_start_state + { + SEL selector; + id object; + id argument; + }; + + static volatile void + __objc_thread_detach_function(struct __objc_thread_start_state *istate) + { + if (istate) { /* Is state valid? */ + id (*imp)(id,SEL,id); + SEL selector = istate->selector; + id object = istate->object; + id argument = istate->argument; + + free(istate); + + if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector))) { + (*imp)(object, selector, argument); + } + else + fprintf(stderr, "__objc_thread_start called with bad selector.\n"); + } + else { + fprintf(stderr, "__objc_thread_start called with NULL state.\n"); + } + objc_thread_exit(); + } + + /******** + * 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; /* Initialial thread state. */ + _objc_thread_t thread_id = NULL; /* Detached thread id. */ + + if (!(istate = (struct __objc_thread_start_state *) + __objc_xmalloc(sizeof(*istate)))) /* Can we allocate state? */ + return NULL; /* No, abort. */ + + istate->selector = selector; /* Initialize the thread's */ + istate->object = object; /* state structure. */ + istate->argument = argument; + + if ((thread_id = objc_thread_create((void *)__objc_thread_detach_function, + istate)) == NULL) { + free(istate); /* Release state if failed. */ + return thread_id; + } + return thread_id; + } + + #undef objc_mutex_lock() + #undef objc_mutex_unlock() + + int + objc_mutex_unlock_x(_objc_mutex_t mutex, const char *f, int l) + { + printf("%16.16s#%4d < unlock", f, l); + return objc_mutex_unlock(mutex); + } + + int + objc_mutex_lock_x(_objc_mutex_t mutex, const char *f, int l) + { + printf("%16.16s#%4d < lock", f, l); + return objc_mutex_lock(mutex); + } + + /***************************************************************************** + * Implementation specific functionality: + */ + + #if defined(__sparc__) && defined(__svr4__) /* Solaris only code. */ + #include "thread-solaris.c" + #elif defined(__sgi__) && defined(__mips__) /* IRIX only code. */ + #include "thread-irix.c" + #elif defined(__alpha__) && defined(__osf__) /* Alpha OSF/1 only code. */ + #include "thread-decosf1.c" + #elif defined(__WIN32__) + #include "thread-win32.c" + #else /* Single threaded code. */ + #include "thread-single.c" + #endif + + /* End of File */ diff -rc2P objc/thread.h objc-threaded/thread.h *** objc/thread.h Wed Dec 31 19:00:00 1969 --- objc-threaded/thread.h Sun Jan 21 19:37:01 1996 *************** *** 0 **** --- 1,68 ---- + /* Thread and mutex controls for Objective C. + Copyright (C) 1993 Free Software Foundation, Inc. + + AUTHOR: Galen C. Hunt (gchunt@cs.rochester.edu) + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) + any later version. + + GNU CC 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. + + You should have received a copy of the GNU General Public License + along with GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* As a special exception, if you link this library with files + compiled with GCC to produce an executable, this does not cause + the resulting executable to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + + + #ifndef __thread_INCLUDE_GNU + #define __thread_INCLUDE_GNU + + #include "objc/objc.h" + + /******** + * Thread safe implementation types and functions. + */ + + #define OBJC_THREAD_INTERACTIVE_PRIORITY 2 + #define OBJC_THREAD_BACKGROUND_PRIORITY 1 + #define OBJC_THREAD_LOW_PRIORITY 0 + + typedef struct _objc_mutex *_objc_mutex_t; + typedef void * _objc_thread_t; + + _objc_mutex_t objc_mutex_allocate(void); + int objc_mutex_deallocate(_objc_mutex_t mutex); + int objc_mutex_lock(_objc_mutex_t mutex); + int objc_mutex_unlock(_objc_mutex_t mutex); + int objc_mutex_trylock(_objc_mutex_t mutex); + + _objc_thread_t objc_thread_create(void (*func)(void *arg), void *arg); + void objc_thread_yield(void); + int objc_thread_exit(void); + int objc_thread_set_priority(int priority); + int objc_thread_get_priority(void); + void * objc_thread_get_data(void); + int objc_thread_set_data(void *value); + _objc_thread_t objc_thread_id(void); + + _objc_thread_t objc_thread_detach(SEL selector, id object, id argument); + int objc_mutex_lock_x(_objc_mutex_t mutex, const char *f, int l); + int objc_mutex_unlock_x(_objc_mutex_t mutex, const char *f, int l); + + /* For debugging of locks, uncomment these two macros: */ + /* #define objc_mutex_lock(x) objc_mutex_lock_x(x, __FILE__, __LINE__) */ + /* #define objc_mutex_unlock(x) objc_mutex_unlock_x(x, __FILE__, __LINE__)*/ + + #endif /* not __thread_INCLUDE_GNU */