Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java


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   
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 
140   public void run() 
141   {
142     nativeRun(funcPtr, funcArg);
143   }
144 
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   */
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   */ 
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   */ 
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. */
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.  */
295   static void deRegisterJoinable(Thread thread) 
296   {
297     joinable.remove(thread);
298   }
299 }
300 
301 // Local Variables:
302 // c-file-style: "gnu"
303 // End: