1 /* GThreadNativeMethodRunner.java -- Implements pthread_create(), under 2 glib's gthread abstraction, for use with GNU Classpath's 3 --portable-native-sync option. 4 This is used by gthread-jni.c 5 6 Copyright (C) 2004, 2005 Free Software Foundation, Inc. 7 8 This file is part of GNU Classpath. 9 10 GNU Classpath is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2, or (at your option) 13 any later version. 14 15 GNU Classpath is distributed in the hope that it will be useful, but 16 WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with GNU Classpath; see the file COPYING. If not, write to the 22 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 23 02110-1301 USA. 24 25 Linking this library statically or dynamically with other modules is 26 making a combined work based on this library. Thus, the terms and 27 conditions of the GNU General Public License cover the whole 28 combination. 29 30 As a special exception, the copyright holders of this library give you 31 permission to link this library with independent modules to produce an 32 executable, regardless of the license terms of these independent 33 modules, and to copy and distribute the resulting executable under 34 terms of your choice, provided that you also meet, for each linked 35 independent module, the terms and conditions of the license of that 36 module. An independent module is a module which is not derived from 37 or based on this library. If you modify this library, you may extend 38 this exception to your version of the library, but you are not 39 obligated to do so. If you do not wish to do so, delete this 40 exception statement from your version. */ 41 42 package gnu.java.awt.peer.gtk; 43 44 import java.lang.ref.WeakReference; 45 import java.util.Collections; 46 import java.util.HashSet; 47 import java.util.Set; 48 49 /** Implements pthread_create(), under glib's gthread abstraction, for use 50 with GNU Classpath's --portable-native-sync option. This is used in 51 gthread-jni.c 52 53 Also implements a registry for threads, mapping Thread objects to small 54 integers. The registry uses weak references for threads that aren't 55 joinable, so that they will be garbage collected. 56 57 There are a number of possible alternative implementations. 58 59 60 The rest of this comment consists of an answer to a question that was 61 raised on the commit-classpath mailing list: 62 63 Mark Wielaard wrote: 64 65 > Can't we assume that jobject and gpointer are both (void *) so we don't 66 > need the int <-> Thread (global jobject ref) mapping? 67 > Maybe there are platforms where jobject and gpointer aren't the same, 68 > but I guess that is pretty unlikely. 69 70 71 I agree with you on the pointer size issues. A gpointer is a void *, so 72 it's certainly guaranteed to be at least as large as any other 73 pointer. And a jobject is implicitly an opaque pointer (in Jikes RVM, we 74 use small integers, but we coerce them into the representation of a 75 pointer). 76 77 The int <==> Thread mapping addresses a different issue. I realize that I 78 did not document this properly (two and a half lines in thread_create), 79 and the point is subtle (at least to me; took me a while to figure out). 80 81 The int => Thread mapping always returns jobjects that are local 82 references, not global ones. This is because Thread objects need to be 83 able to go away and be garbage collected after the thread they refer to 84 has died. 85 86 If we keep a global object reference to a thread, then when do we delete 87 that global object reference? We have an answer in the case of GThread 88 objects that were explicitly created with the joinable attribute. It is 89 safe for us to maintain a global reference to any joinable thread, since 90 the joinable thread must linger (even if only in a zombie state) 91 until it's explicitly joined via a g_thread_join() call. The global ref 92 could be cleaned up at that point too. 93 94 However, in the case of GThreads that were created non-joinable by 95 g_thread_create(), and in the case of Java threads that were created 96 within pure Java code (not via g_thread_create()), we don't want them to 97 linger forever, and there is no way to tell when the last reference 98 to such threads needs to expire. In the case of this application -- AWT 99 with GTK peers -- it would probably be safe anyway, since there are not 100 very many threads we create, but I was going for correctness even in the 101 case of long-running programs that might set up and tear down AWT 102 interfaces many times. 103 104 So, I duplicated the POSIX thread-ID semantics. The thread ID of a 105 non-joinable thread remains valid as long as that thread is still alive. 106 Once that thread dies, the old thread ID may be reused at any moment. And 107 that's why the array indexed by thread ID numbers is an array of weak 108 references. 109 110 That's also why the int => Thread jobject mapping function always returns 111 local references, since global references would lock the Thread in memory 112 forever. 113 114 I would dearly love there to be a cleaner solution. I dislike the 115 repeated dips from C code into Java that are necessary to look up thread 116 ID numbers. If anyone can think of one, I'm all ears. 117 */ 118 119 class GThreadNativeMethodRunner 120 extends Thread 121 { 122 /** The C function pointer that was passed to g_thread_create(). 123 Specifically, this the numeric address of an object of 124 C type "void *(*funcPtr)(void *funcArg)". 125 */ 126 private final long funcPtr; 127 128 /** The argument for the function "funcPtr(funcArg)". */ 129 private final long funcArg; 130 GThreadNativeMethodRunner(long funcPtr, long funcArg, boolean joinable)131 GThreadNativeMethodRunner(long funcPtr, long funcArg, boolean joinable) 132 { 133 this.funcPtr = funcPtr; 134 this.funcArg = funcArg; 135 136 if (joinable) 137 registerSelfJoinable(); 138 } 139 run()140 public void run() 141 { 142 nativeRun(funcPtr, funcArg); 143 } 144 nativeRun(long funcPtr, long funcArg)145 private native void nativeRun(long funcPtr, long funcArg); 146 147 /** THREADS is an array of threads, indexed by thread ID codes. Not sure 148 whether this is the "best" approach but it does make it O(1) to look up a 149 thread by its ID. 150 151 Zero is a valid thread ID code. Any negative number is invalid. 152 153 Possible future fixes (TODO?) 154 155 - The THREADS array will only grow. probably not a problem. 156 But we could keep count when nulling entries and shrink when we have 157 lots of nulls at the end. Probably not worth it. --mjw 158 159 - Could make this a set of Object; see the comment on "joinable" below. 160 161 The initial size of 17 is just a starting point. Any number will do, 162 including zero. 163 */ 164 private static WeakReference[] threads = new WeakReference[17]; 165 166 /** Used by threadToThreadID, below. Returns the registration number of 167 the newly-registered thread. 168 */ registerThread(Thread t)169 private static synchronized int registerThread(Thread t) 170 { 171 int i; 172 173 for (i = 0; i < threads.length; ++i) 174 { 175 WeakReference ref = threads[i]; 176 if (ref == null) 177 break; // found an empty spot. 178 } 179 180 if (i == threads.length) 181 { 182 /* expand the array */ 183 WeakReference[] bigger = new WeakReference[threads.length * 2]; 184 System.arraycopy(threads, 0, bigger, 0, threads.length); 185 threads = bigger; 186 } 187 188 threads[i] = new WeakReference(t); 189 190 return i; 191 } 192 193 /** Look up the Thread ID # for a Thread. Assign a Thread ID # if none 194 exists. This is a general routine for handling all threads, including 195 the VM's main thread, if appropriate. 196 197 198 Runs in O(n/2) time. 199 200 We can't just issue a threadID upon thread creation. If we were to do 201 that, not all threads would have a threadID, because not all threads 202 are launched by GThreadNativeMethodRunner. 203 */ threadToThreadID(Thread t)204 static synchronized int threadToThreadID(Thread t) 205 { 206 for (int i = 0; i < threads.length; ++i ) 207 { 208 if (threads[i] == null) 209 continue; 210 Thread referent = (Thread) threads[i].get(); 211 if (referent == null) 212 { 213 threads[i] = null; // Purge the dead WeakReference. 214 continue; 215 } 216 if (referent.equals(t)) 217 return i; 218 } // for() 219 220 /* No match found. */ 221 return registerThread(t); 222 } 223 224 /** @param threadID Must be a non-negative integer. 225 226 Used to return null if the thread number was out of range or if 227 the thread was unregistered. Now we throw an exception. 228 229 Possible Alternative Interface: We could go back to returning null in 230 some sort of check-free mode, so code that calls this function must 231 be prepared to get null. 232 */ threadIDToThread(int threadID)233 static Thread threadIDToThread(int threadID) 234 throws IllegalArgumentException 235 { 236 if (threadID < 0) 237 throw new IllegalArgumentException("Received a negative threadID, " 238 + threadID); 239 if (threadID >= threads.length) 240 throw new IllegalArgumentException("Received a threadID (" + threadID 241 + ") higher than was" 242 + " ever issued"); 243 244 /* Note: if the user is using a stale reference, things will just 245 break. We might end up getting a different thread than the one 246 expected. 247 248 TODO: Add an error-checking mode where the user's problems with threads 249 are announced. For instance, if the user asks for the thread 250 associated with a threadID that was never issued, we could print a 251 warning or even abort. 252 253 TODO: Consider optionally disabling all of the error-checking we 254 already have; it probably slows down the implementation. We could 255 just return NULL. This is just the reverse of the above TODO item. 256 */ 257 258 WeakReference threadRef = threads[threadID]; 259 260 if (threadRef == null) 261 throw new IllegalArgumentException("Asked to look up a stale or unissued" 262 + "threadID (" + threadID + ")" ); 263 264 265 Thread referent = (Thread) threadRef.get(); 266 if (referent == null) 267 throw new IllegalArgumentException ("Asked to look up a stale threadID (" 268 + threadID + ")"); 269 return referent; 270 } 271 272 /** Joinable threads need a hard reference, so that they won't go away when 273 they die. That is because their thread IDs need to stay valid until the 274 thread is joined via thread_join(threadID). Joinable threads have to be 275 explicitly joined before they are allowed to go away completely. 276 277 Possible Alternative Implementation: Eliminate the Joinable set. When 278 calling getThreadIDFromThread() you know whether or not the thread 279 is joinable. So just store the Thread itself in the threads array? 280 Make that array an Object array and check with instanceof. This 281 looks cleaner and more robust to me and it saves a native -> Java 282 call. But instanceof might be expensive. --mjw 283 */ 284 private static final Set joinable = 285 Collections.synchronizedSet(new HashSet()); 286 287 /** Only called from the constructor. */ registerSelfJoinable()288 private void registerSelfJoinable() 289 { 290 joinable.add(this); 291 } 292 293 /** This method is only called from JNI, and only after we have succeeded in 294 a thread_join() operation. */ deRegisterJoinable(Thread thread)295 static void deRegisterJoinable(Thread thread) 296 { 297 joinable.remove(thread); 298 } 299 } 300 301 // Local Variables: 302 // c-file-style: "gnu" 303 // End: 304