From 61341707b3db764c90f954097bf89b0811f92965 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 2 Sep 2004 06:58:08 +0000 Subject: missing added files from merge From-SVN: r86958 --- libjava/gnu/java/awt/peer/gtk/GThreadMutex.java | 109 ++++++++ .../awt/peer/gtk/GThreadNativeMethodRunner.java | 302 +++++++++++++++++++++ 2 files changed, 411 insertions(+) create mode 100644 libjava/gnu/java/awt/peer/gtk/GThreadMutex.java create mode 100644 libjava/gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java (limited to 'libjava/gnu') diff --git a/libjava/gnu/java/awt/peer/gtk/GThreadMutex.java b/libjava/gnu/java/awt/peer/gtk/GThreadMutex.java new file mode 100644 index 0000000..111c9a8 --- /dev/null +++ b/libjava/gnu/java/awt/peer/gtk/GThreadMutex.java @@ -0,0 +1,109 @@ +/* GThreadMutex.java -- Implements a mutex object for glib's gthread + abstraction, for use with GNU Classpath's --portable-native-sync option. + This is used in gthread-jni.c + + Copyright (C) 2004 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; + +/** Implements a mutex object for glib's gthread + abstraction, for use with GNU Classpath's --portable-native-sync option. + This is used in gthread-jni.c. + + We use this object to implement the POSIX semantics for Mutexes. They are + needed are needed for the function vector that is passed to glib's + g_thread subpackage's initialization function. + + The GThreadMutex object itself serves as the Real Lock; if code has + entered the monitor for this GThreadMutex object (in Java language, if + it's synchronized on this object) then it holds the lock that this object + represents. + + @author Steven Augart + May, 2004 + + +*/ + +class GThreadMutex +{ + /** Might "lock" be locked? Is anyone waiting + to get that lock? How long is the queue? + + If zero, nobody holds a lock on this GThreadMutex object, and nobody is + trying to get one. Before someone attempts to acquire a lock on this + object, they must increment potentialLockers. After they release their + lock on this object, they must decrement potentialLockers. + + Access to this field is guarded by synchronizing on the object + lockForPotentialLockers. + + After construction, we only access this field via JNI. + */ + volatile int potentialLockers; + + /** An object to synchronize to if you want to examine or modify the + potentialLockers field. Only hold this lock for brief + moments, just long enough to check or set the value of + lockForPotentialLockers. + + We use this representation so that g_thread_mutex_trylock() will work + with the POSIX semantics. This is the only case in which you ever hold a + lock on lockForPotentialLockers while trying to get another + lock -- if you are the mutex_trylock() implementation, and you have just + checked that potentialLockers has the value zero. In that + case, mutex_trylock() holds the lock on lockForPotentialLockers so that + another thread calling mutex_trylock() or mutex_lock() won't increment + potentialLockers after we've checked it and before we've gained the lock + on the POSIX mutex. Of course, in that case the operation of gaining + the POSIX lock itself will succeed immediately, and once it has + succeeded, trylock releases lockForPotentialLockers right away, + incremented to 1 (one). + + After construction, we only access this field via JNI. + */ + Object lockForPotentialLockers; + + GThreadMutex() + { + potentialLockers = 0; + lockForPotentialLockers = new Object(); + } +} +// Local Variables: +// c-file-style: "gnu" +// End: diff --git a/libjava/gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java b/libjava/gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java new file mode 100644 index 0000000..a4cb35a --- /dev/null +++ b/libjava/gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java @@ -0,0 +1,302 @@ +/* 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 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.Set; +import java.util.Collections; +import java.util.HashSet; + +/** 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: -- cgit v1.1