| /* GThreadNativeMethodRunner.java -- Implements pthread_create(), under |
| glib's gthread abstraction, for use with GNU Classpath's |
| --portable-native-sync option. |
| This is used by gthread-jni.c |
| |
| Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath 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 Classpath 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 Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.java.awt.peer.gtk; |
| |
| import java.lang.ref.WeakReference; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** Implements pthread_create(), under glib's gthread abstraction, for use |
| with GNU Classpath's --portable-native-sync option. This is used in |
| gthread-jni.c |
| |
| Also implements a registry for threads, mapping Thread objects to small |
| integers. The registry uses weak references for threads that aren't |
| joinable, so that they will be garbage collected. |
| |
| There are a number of possible alternative implementations. |
| |
| |
| The rest of this comment consists of an answer to a question that was |
| raised on the commit-classpath mailing list: |
| |
| Mark Wielaard wrote: |
| |
| > Can't we assume that jobject and gpointer are both (void *) so we don't |
| > need the int <-> Thread (global jobject ref) mapping? |
| > Maybe there are platforms where jobject and gpointer aren't the same, |
| > but I guess that is pretty unlikely. |
| |
| |
| I agree with you on the pointer size issues. A gpointer is a void *, so |
| it's certainly guaranteed to be at least as large as any other |
| pointer. And a jobject is implicitly an opaque pointer (in Jikes RVM, we |
| use small integers, but we coerce them into the representation of a |
| pointer). |
| |
| The int <==> Thread mapping addresses a different issue. I realize that I |
| did not document this properly (two and a half lines in thread_create), |
| and the point is subtle (at least to me; took me a while to figure out). |
| |
| The int => Thread mapping always returns jobjects that are local |
| references, not global ones. This is because Thread objects need to be |
| able to go away and be garbage collected after the thread they refer to |
| has died. |
| |
| If we keep a global object reference to a thread, then when do we delete |
| that global object reference? We have an answer in the case of GThread |
| objects that were explicitly created with the joinable attribute. It is |
| safe for us to maintain a global reference to any joinable thread, since |
| the joinable thread must linger (even if only in a zombie state) |
| until it's explicitly joined via a g_thread_join() call. The global ref |
| could be cleaned up at that point too. |
| |
| However, in the case of GThreads that were created non-joinable by |
| g_thread_create(), and in the case of Java threads that were created |
| within pure Java code (not via g_thread_create()), we don't want them to |
| linger forever, and there is no way to tell when the last reference |
| to such threads needs to expire. In the case of this application -- AWT |
| with GTK peers -- it would probably be safe anyway, since there are not |
| very many threads we create, but I was going for correctness even in the |
| case of long-running programs that might set up and tear down AWT |
| interfaces many times. |
| |
| So, I duplicated the POSIX thread-ID semantics. The thread ID of a |
| non-joinable thread remains valid as long as that thread is still alive. |
| Once that thread dies, the old thread ID may be reused at any moment. And |
| that's why the array indexed by thread ID numbers is an array of weak |
| references. |
| |
| That's also why the int => Thread jobject mapping function always returns |
| local references, since global references would lock the Thread in memory |
| forever. |
| |
| I would dearly love there to be a cleaner solution. I dislike the |
| repeated dips from C code into Java that are necessary to look up thread |
| ID numbers. If anyone can think of one, I'm all ears. |
| */ |
| |
| class GThreadNativeMethodRunner |
| extends Thread |
| { |
| /** The C function pointer that was passed to g_thread_create(). |
| Specifically, this the numeric address of an object of |
| C type "void *(*funcPtr)(void *funcArg)". |
| */ |
| private final long funcPtr; |
| |
| /** The argument for the function "funcPtr(funcArg)". */ |
| private final long funcArg; |
| |
| GThreadNativeMethodRunner(long funcPtr, long funcArg, boolean joinable) |
| { |
| this.funcPtr = funcPtr; |
| this.funcArg = funcArg; |
| |
| if (joinable) |
| registerSelfJoinable(); |
| } |
| |
| public void run() |
| { |
| nativeRun(funcPtr, funcArg); |
| } |
| |
| private native void nativeRun(long funcPtr, long funcArg); |
| |
| /** THREADS is an array of threads, indexed by thread ID codes. Not sure |
| whether this is the "best" approach but it does make it O(1) to look up a |
| thread by its ID. |
| |
| Zero is a valid thread ID code. Any negative number is invalid. |
| |
| Possible future fixes (TODO?) |
| |
| - The THREADS array will only grow. probably not a problem. |
| But we could keep count when nulling entries and shrink when we have |
| lots of nulls at the end. Probably not worth it. --mjw |
| |
| - Could make this a set of Object; see the comment on "joinable" below. |
| |
| The initial size of 17 is just a starting point. Any number will do, |
| including zero. |
| */ |
| private static WeakReference[] threads = new WeakReference[17]; |
| |
| /** Used by threadToThreadID, below. Returns the registration number of |
| the newly-registered thread. |
| */ |
| private static synchronized int registerThread(Thread t) |
| { |
| int i; |
| |
| for (i = 0; i < threads.length; ++i) |
| { |
| WeakReference ref = threads[i]; |
| if (ref == null) |
| break; // found an empty spot. |
| } |
| |
| if (i == threads.length) |
| { |
| /* expand the array */ |
| WeakReference[] bigger = new WeakReference[threads.length * 2]; |
| System.arraycopy(threads, 0, bigger, 0, threads.length); |
| threads = bigger; |
| } |
| |
| threads[i] = new WeakReference(t); |
| |
| return i; |
| } |
| |
| /** Look up the Thread ID # for a Thread. Assign a Thread ID # if none |
| exists. This is a general routine for handling all threads, including |
| the VM's main thread, if appropriate. |
| |
| |
| Runs in O(n/2) time. |
| |
| We can't just issue a threadID upon thread creation. If we were to do |
| that, not all threads would have a threadID, because not all threads |
| are launched by GThreadNativeMethodRunner. |
| */ |
| static synchronized int threadToThreadID(Thread t) |
| { |
| for (int i = 0; i < threads.length; ++i ) |
| { |
| if (threads[i] == null) |
| continue; |
| Thread referent = (Thread) threads[i].get(); |
| if (referent == null) |
| { |
| threads[i] = null; // Purge the dead WeakReference. |
| continue; |
| } |
| if (referent.equals(t)) |
| return i; |
| } // for() |
| |
| /* No match found. */ |
| return registerThread(t); |
| } |
| |
| /** @param threadID Must be a non-negative integer. |
| |
| Used to return null if the thread number was out of range or if |
| the thread was unregistered. Now we throw an exception. |
| |
| Possible Alternative Interface: We could go back to returning null in |
| some sort of check-free mode, so code that calls this function must |
| be prepared to get null. |
| */ |
| static Thread threadIDToThread(int threadID) |
| throws IllegalArgumentException |
| { |
| if (threadID < 0) |
| throw new IllegalArgumentException("Received a negative threadID, " |
| + threadID); |
| if (threadID >= threads.length) |
| throw new IllegalArgumentException("Received a threadID (" + threadID |
| + ") higher than was" |
| + " ever issued"); |
| |
| /* Note: if the user is using a stale reference, things will just |
| break. We might end up getting a different thread than the one |
| expected. |
| |
| TODO: Add an error-checking mode where the user's problems with threads |
| are announced. For instance, if the user asks for the thread |
| associated with a threadID that was never issued, we could print a |
| warning or even abort. |
| |
| TODO: Consider optionally disabling all of the error-checking we |
| already have; it probably slows down the implementation. We could |
| just return NULL. This is just the reverse of the above TODO item. |
| */ |
| |
| WeakReference threadRef = threads[threadID]; |
| |
| if (threadRef == null) |
| throw new IllegalArgumentException("Asked to look up a stale or unissued" |
| + "threadID (" + threadID + ")" ); |
| |
| |
| Thread referent = (Thread) threadRef.get(); |
| if (referent == null) |
| throw new IllegalArgumentException ("Asked to look up a stale threadID (" |
| + threadID + ")"); |
| return referent; |
| } |
| |
| /** Joinable threads need a hard reference, so that they won't go away when |
| they die. That is because their thread IDs need to stay valid until the |
| thread is joined via thread_join(threadID). Joinable threads have to be |
| explicitly joined before they are allowed to go away completely. |
| |
| Possible Alternative Implementation: Eliminate the Joinable set. When |
| calling getThreadIDFromThread() you know whether or not the thread |
| is joinable. So just store the Thread itself in the threads array? |
| Make that array an Object array and check with instanceof. This |
| looks cleaner and more robust to me and it saves a native -> Java |
| call. But instanceof might be expensive. --mjw |
| */ |
| private static final Set joinable = |
| Collections.synchronizedSet(new HashSet()); |
| |
| /** Only called from the constructor. */ |
| private void registerSelfJoinable() |
| { |
| joinable.add(this); |
| } |
| |
| /** This method is only called from JNI, and only after we have succeeded in |
| a thread_join() operation. */ |
| static void deRegisterJoinable(Thread thread) |
| { |
| joinable.remove(thread); |
| } |
| } |
| |
| // Local Variables: |
| // c-file-style: "gnu" |
| // End: |