00001 /*! 00002 * @file threadutil.c 00003 * 00004 * @brief Utilities for operating the JVM thread state model on this 00005 * real machine implementation. 00006 * 00007 * @todo Timers for Thread.sleep() and Thread.wait() and Object.wait() 00008 * that use millisecond timers @e are supported. The variation 00009 * that supports higher resolution of milliseconds and nanoseconds 00010 * are @e not supported, but the millisecond version is used instead. 00011 * 00012 * @internal This file also serves the dual purpose as a catch-all for 00013 * development experiments. Due to the fact that the implementation 00014 * of the Java thread and the supporting rthread structure is deeply 00015 * embedded in the core of the development of this software, this 00016 * file has contents that come and go during development. Some 00017 * functions get staged here before deciding where they @e really 00018 * go; some are interim functions for debugging, some were glue 00019 * that eventually went away. Be careful to remove prototypes 00020 * to such functions from the appropriate header file. 00021 * 00022 * 00023 * @section Control 00024 * 00025 * \$URL: https://svn.apache.org/path/name/threadutil.c $ \$Id: threadutil.c 0 09/28/2005 dlydick $ 00026 * 00027 * Copyright 2005 The Apache Software Foundation 00028 * or its licensors, as applicable. 00029 * 00030 * Licensed under the Apache License, Version 2.0 ("the License"); 00031 * you may not use this file except in compliance with the License. 00032 * You may obtain a copy of the License at 00033 * 00034 * http://www.apache.org/licenses/LICENSE-2.0 00035 * 00036 * Unless required by applicable law or agreed to in writing, 00037 * software distributed under the License is distributed on an 00038 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 00039 * either express or implied. 00040 * 00041 * See the License for the specific language governing permissions 00042 * and limitations under the License. 00043 * 00044 * @version \$LastChangedRevision: 0 $ 00045 * 00046 * @date \$LastChangedDate: 09/28/2005 $ 00047 * 00048 * @author \$LastChangedBy: dlydick $ 00049 * Original code contributed by Daniel Lydick on 09/28/2005. 00050 * 00051 * @section Reference 00052 * 00053 */ 00054 00055 #include "arch.h" 00056 ARCH_COPYRIGHT_APACHE(threadutil, c, "$URL: https://svn.apache.org/path/name/threadutil.c $ $Id: threadutil.c 0 09/28/2005 dlydick $"); 00057 00058 00059 #include "jvmcfg.h" 00060 #include "cfmacros.h" 00061 #include "classfile.h" 00062 #include "jvm.h" 00063 #include "jvmclass.h" 00064 #include "util.h" 00065 00066 00067 /*! 00068 * @brief Update the interval timer for this thread from 00069 * @c @b java.lang.Thread.sleep() or from a timed 00070 * @c @b java.lang.Thread.wait() or @c @b java.lang.Thread.join(). 00071 * 00072 * This function is designed to be invoked from the timeslice interrupt 00073 * handler and @e only from thence. It DOES NOT handle 00074 * (millisec, nanosec) resolution, only millisecond resolution. 00075 * 00076 * 00077 * @b Parameters: @link #rvoid rvoid@endlink 00078 * 00079 * 00080 * @returns @link #rvoid rvoid@endlink 00081 * 00082 */ 00083 00084 rvoid threadutil_update_sleeptime_interval(rvoid) 00085 { 00086 jvm_thread_index thridx; 00087 00088 /* Lock out the @e world during timer update */ 00089 pthread_mutex_lock(&pjvm->sleeplock); 00090 00091 for (thridx = jvm_thread_index_null; 00092 thridx < JVMCFG_MAX_THREADS; 00093 thridx++) 00094 { 00095 if ((THREAD_STATUS_INUSE & THREAD(thridx).status) && 00096 ((THREAD_STATUS_SLEEP | 00097 THREAD_STATUS_JOINTIMED | 00098 THREAD_STATUS_WAITTIMED ) & THREAD(thridx).status)) 00099 { 00100 /* 00101 * Perform next interval update. Stop decrementing 00102 * when time reaches zero. 00103 */ 00104 if (0 != THREAD(thridx).sleeptime) 00105 { 00106 THREAD(thridx).sleeptime--; 00107 } 00108 } 00109 } 00110 00111 /* Unlock the @e world after timer update */ 00112 pthread_mutex_unlock(&pjvm->sleeplock); 00113 00114 return; 00115 00116 } /* END of threadutil_update_sleeptime_interval() */ 00117 00118 00119 /*! 00120 * @brief Complete the UNtimed Thread.join() request and allow threads 00121 * that have joined this one to resume execution. 00122 * 00123 * This function is typically called when a thread enters the 00124 * @b COMPLETE state after finishing its JVM execution. 00125 * 00126 * Review state of thread table, looking for the following conditions. 00127 * For those that meet them, move thread out of given state and 00128 * forward to next state. Three functions are used, depending on 00129 * the current state, threadutil_update_blockingevent() and 00130 * threadutil_update_wait() and threadutil_update_lock(): 00131 * 00132 * @verbatim 00133 00134 Condition: Current state: Next state: threadutil_update_YYY: 00135 ---------- -------------- ----------- ---------------------- 00136 00137 Thread.join() COMPLETE N/C _blockingevents() 00138 (forever, where 00139 current thread 00140 is COMPLETE, and 00141 target thread is 00142 BLOCKED, and is 00143 moved to UNBLOCKED) 00144 00145 Thread.join(n) COMPLETE N/C _blockingevents() 00146 (timed, where 00147 n has expired 00148 on current 00149 thread or it is 00150 COMPLETE, and 00151 target thread 00152 is BLOCKED, and 00153 is moved to 00154 UNBLOCKED) 00155 00156 Thread.sleep(n) BLOCKED UNBLOCKED _blockingevents() 00157 (n has expired on 00158 current thread) 00159 00160 Interruptible I/O BLOCKED UNBLOCKED _blockingevents() 00161 from class 00162 java.nio.channels 00163 .InterruptibleChannel 00164 00165 Object.wait() WAIT NOTIFY _wait() 00166 (forever on 00167 current thread, 00168 where target object 00169 lock was released) 00170 00171 Object.wait(n) WAIT NOTIFY _wait() 00172 (timed, where 00173 @c @b n 00174 has expired on 00175 current thread 00176 or target object 00177 lock was released) 00178 00179 One of: LOCK ACQUIRE _lock() 00180 Object.notify() 00181 Object.notifyAll() 00182 Thread.interrupt() 00183 synchronized(Object) 00184 ... put thread 00185 into LOCK state. 00186 Now it will 00187 negotiate to 00188 ACQUIRE its 00189 object's 00190 monitor lock. 00191 00192 00193 Thread.suspend() ANY BLOCKED threadutil_suspend() 00194 00195 Thread.resume() BLOCKED UNBLOCKED threadutil_resume() 00196 moves a 00197 Thread.suspend() 00198 thread forward 00199 to UNBLOCKED 00200 00201 @endverbatim 00202 * 00203 * With the exception of threadutil_suspend() and threadutil_resume(), 00204 * these functions is designed to be invoked from the JVM outer loop. 00205 * CAVEAT EMPTOR: Those two functions are deprecated. Use 00206 * at your own risk! 00207 * 00208 * @todo Interruptible from class 00209 * @c @b java.nio.channels.InterruptibleChannel 00210 * 00211 * 00212 * @param thridxcurr Thread for which to examine state changes 00213 * 00214 * 00215 * @returns @link #rvoid rvoid@endlink 00216 * 00217 */ 00218 00219 rvoid threadutil_update_blockingevents(jvm_thread_index thridxcurr) 00220 { 00221 /* Only examine INUSE threads in selected states */ 00222 if (!(THREAD_STATUS_INUSE & THREAD(thridxcurr).status)) 00223 { 00224 return; 00225 } 00226 00227 switch (THREAD(thridxcurr).this_state) 00228 { 00229 case THREAD_STATE_COMPLETE: /* Untimed/untimed Thread.join() */ 00230 00231 case THREAD_STATE_BLOCKED: /* Thread.sleep() and */ 00232 /*! @todo interruptible I/O req's*/ 00233 00234 default: /* Not meaningful for this logic */ 00235 return; 00236 } 00237 00238 /* sleep() -- time period expired for only this thread */ 00239 if ((THREAD_STATE_BLOCKED == THREAD(thridxcurr).this_state) && 00240 (THREAD_STATUS_SLEEP & THREAD(thridxcurr).status) && 00241 (0 == timeslice_get_thread_sleeptime(thridxcurr))) 00242 { 00243 /* Mark thread to continue now after sleep() */ 00244 THREAD(thridxcurr).status &= ~THREAD_STATUS_SLEEP; 00245 threadstate_request_unblocked(thridxcurr); 00246 00247 /* 00248 * Check if Thread.interrupt() was thrown 00249 * against this thread. 00250 * 00251 * Also need to throw the exception in JVM outer loop. 00252 */ 00253 if (THREAD_STATUS_INTERRUPTED & THREAD(thridxcurr).status) 00254 { 00255 THREAD(thridxcurr).status &= ~THREAD_STATUS_INTERRUPTED; 00256 00257 THREAD(thridxcurr).status |= THREAD_STATUS_THREW_EXCEPTION; 00258 00259 THREAD(thridxcurr).pThrowableEvent = 00260 JVMCLASS_JAVA_LANG_INTERRUPTEDEXCEPTION; 00261 } 00262 } 00263 else 00264 00265 /*! @todo First pass guess at interruptible I/O */ 00266 if ((THREAD_STATE_BLOCKED == THREAD(thridxcurr).this_state) && 00267 (THREAD_STATUS_INTERRUPTIBLEIO & THREAD(thridxcurr).status)) 00268 { 00269 /*! 00270 * Check if Thread.interrupt() was thrown 00271 * against the interruptible I/O operation on this thread. 00272 * 00273 * @todo Is this the correct way to handle 00274 * interruptible I/O events? 00275 * Also need to throw the exception in JVM outer loop. 00276 */ 00277 if (THREAD_STATUS_INTERRUPTED & THREAD(thridxcurr).status) 00278 { 00279 /* Mark thread to continue now after Thread.interrupt() */ 00280 THREAD(thridxcurr).status &= ~THREAD_STATUS_INTERRUPTIBLEIO; 00281 threadstate_request_unblocked(thridxcurr); 00282 00283 THREAD(thridxcurr).status &= ~THREAD_STATUS_INTERRUPTED; 00284 00285 THREAD(thridxcurr).status |= THREAD_STATUS_THREW_EXCEPTION; 00286 00287 THREAD(thridxcurr).pThrowableEvent = 00288 JVMCLASS_JAVA_NIO_CHANNELS_CLOSEDBYINTERRUPTEXCEPTION; 00289 } 00290 } 00291 else 00292 00293 /* 00294 * Examine Thread.join() conditions, both timed and untimed 00295 */ 00296 if (THREAD_STATE_COMPLETE == THREAD(thridxcurr).this_state) 00297 { 00298 jvm_thread_index thridxjoin; 00299 00300 /* Skip JVMCFG_NULL_THREAD */ 00301 for (thridxjoin = JVMCFG_SYSTEM_THREAD; 00302 thridxjoin < JVMCFG_MAX_THREADS; 00303 thridxjoin++) 00304 { 00305 /* 00306 * Skip myself 00307 */ 00308 if (thridxcurr == thridxjoin) 00309 { 00310 continue; 00311 } 00312 00313 /* Only process threads in the BLOCKED state */ 00314 if (THREAD_STATE_BLOCKED != THREAD(thridxjoin).this_state) 00315 { 00316 continue; 00317 } 00318 00319 /* 00320 * If current COMPLETE thread is the target of a join, 00321 * check which type it is, timed or untimed. 00322 */ 00323 if (thridxcurr == THREAD(thridxjoin).jointarget) 00324 { 00325 /* UNtimed join() -- where time period is zero/forever*/ 00326 if ((THREAD_STATUS_JOIN4EVER & 00327 THREAD(thridxjoin).status)) 00328 { 00329 /* Mark joined thread to be UNBLOCKED after join()*/ 00330 THREAD(thridxjoin).jointarget = 00331 jvm_thread_index_null; 00332 THREAD(thridxjoin).status &= 00333 ~THREAD_STATUS_JOIN4EVER; 00334 threadstate_request_unblocked(thridxjoin); 00335 00336 /* 00337 * Check if Thread.interrupted() was thrown 00338 * against this thread. 00339 */ 00340 if (THREAD_STATUS_INTERRUPTED & 00341 THREAD(thridxjoin).status) 00342 { 00343 THREAD(thridxjoin).status &= 00344 ~THREAD_STATUS_INTERRUPTED; 00345 00346 THREAD(thridxjoin).status |= 00347 THREAD_STATUS_THREW_EXCEPTION; 00348 00349 THREAD(thridxjoin).pThrowableEvent = 00350 JVMCLASS_JAVA_LANG_INTERRUPTEDEXCEPTION; 00351 } 00352 00353 } 00354 else 00355 00356 /* TIMED join() --time period is NON-zero,now expired */ 00357 if ((THREAD_STATUS_JOINTIMED & 00358 THREAD(thridxjoin).status) && 00359 (0 == timeslice_get_thread_sleeptime(thridxjoin))) 00360 { 00361 /* Mark joined thread to be UNBLOCKED after join()*/ 00362 THREAD(thridxjoin).jointarget = 00363 jvm_thread_index_null; 00364 THREAD(thridxjoin).status &= 00365 ~THREAD_STATUS_JOINTIMED; 00366 threadstate_request_unblocked(thridxjoin); 00367 00368 /* 00369 * Check if Thread.interrupted() was thrown 00370 * against this thread. 00371 */ 00372 if (THREAD_STATUS_INTERRUPTED & 00373 THREAD(thridxjoin).status) 00374 { 00375 THREAD(thridxjoin).status &= 00376 ~THREAD_STATUS_INTERRUPTED; 00377 00378 THREAD(thridxjoin).status |= 00379 THREAD_STATUS_THREW_EXCEPTION; 00380 00381 THREAD(thridxjoin).pThrowableEvent = 00382 JVMCLASS_JAVA_LANG_INTERRUPTEDEXCEPTION; 00383 } 00384 } 00385 } 00386 } 00387 } 00388 00389 return; 00390 00391 } /* END of threadutil_update_blockingevents() */ 00392 00393 00394 rvoid threadutil_update_wait(jvm_thread_index thridxcurr) 00395 { 00396 /* Only examine INUSE threads in WAIT state */ 00397 if (!(THREAD_STATUS_INUSE & THREAD(thridxcurr).status)) 00398 { 00399 return; 00400 } 00401 00402 switch (THREAD(thridxcurr).this_state) 00403 { 00404 case THREAD_STATE_WAIT: /* Timed/untimed Object.wait() */ 00405 break; 00406 00407 default: /* Not meaningful for this logic */ 00408 return; 00409 } 00410 00411 00412 /* wait() -- Check for notify() or notifyAll() or interrupt() */ 00413 if (THREAD_STATUS_WAIT4EVER & THREAD(thridxcurr).status) 00414 { 00415 /* 00416 * Check if Thread.interrupt() was thrown against this thread 00417 * or if Object.notify() against this object by this thread. 00418 * 00419 * (Also need to throw the exception in JVM outer loop.) 00420 */ 00421 if (THREAD_STATUS_INTERRUPTED & THREAD(thridxcurr).status) 00422 { 00423 THREAD(thridxcurr).status &= ~THREAD_STATUS_INTERRUPTED; 00424 00425 THREAD(thridxcurr).status |= THREAD_STATUS_THREW_EXCEPTION; 00426 00427 THREAD(thridxcurr).pThrowableEvent = 00428 JVMCLASS_JAVA_LANG_INTERRUPTEDEXCEPTION; 00429 00430 /* Remove WAIT condition and move to NOTIFY state */ 00431 THREAD(thridxcurr).status &= ~THREAD_STATUS_WAIT4EVER; 00432 threadstate_request_notify(thridxcurr); 00433 } 00434 else 00435 if (THREAD_STATUS_NOTIFIED & THREAD(thridxcurr).status) 00436 { 00437 THREAD(thridxcurr).status &= ~THREAD_STATUS_NOTIFIED; 00438 00439 /* Remove WAIT condition and move to NOTIFY state */ 00440 THREAD(thridxcurr).status &= ~THREAD_STATUS_WAIT4EVER; 00441 threadstate_request_notify(thridxcurr); 00442 } 00443 } 00444 else 00445 00446 /* wait(n) -- TIMED, chk if monitor lock released on target object 00447 or if timer expired */ 00448 if ((THREAD_STATUS_WAITTIMED & THREAD(thridxcurr).status) && 00449 (jvm_object_hash_null != THREAD(thridxcurr).locktarget)) 00450 { 00451 /* Give up if timer expired */ 00452 if (0 == timeslice_get_thread_sleeptime(thridxcurr)) 00453 { 00454 /* Remove @b WAIT condition and move to next state */ 00455 THREAD(thridxcurr).status &= ~THREAD_STATUS_WAITTIMED; 00456 threadstate_request_notify(thridxcurr); 00457 } 00458 else 00459 { 00460 /* 00461 * Check if Thread.interrupt() was thrown against this 00462 * thread or if Object.notify() against this object by this 00463 * thread. 00464 * 00465 * (Also need to throw the exception in JVM outer loop.) 00466 */ 00467 if (THREAD_STATUS_INTERRUPTED & THREAD(thridxcurr).status) 00468 { 00469 THREAD(thridxcurr).status &= ~THREAD_STATUS_INTERRUPTED; 00470 00471 THREAD(thridxcurr).status |= 00472 THREAD_STATUS_THREW_EXCEPTION; 00473 00474 THREAD(thridxcurr).pThrowableEvent = 00475 JVMCLASS_JAVA_LANG_INTERRUPTEDEXCEPTION; 00476 00477 /* Remove WAIT condition and move to NOTIFY state */ 00478 THREAD(thridxcurr).status &= ~THREAD_STATUS_WAITTIMED; 00479 threadstate_request_notify(thridxcurr); 00480 } 00481 else 00482 if (THREAD_STATUS_NOTIFIED & THREAD(thridxcurr).status) 00483 { 00484 THREAD(thridxcurr).status &= ~THREAD_STATUS_NOTIFIED; 00485 00486 /* Remove WAIT condition and move to NOTIFY state */ 00487 THREAD(thridxcurr).status &= ~THREAD_STATUS_WAITTIMED; 00488 threadstate_request_notify(thridxcurr); 00489 } 00490 } 00491 } 00492 00493 return; 00494 00495 } /* END of threadutil_update_wait() */ 00496 00497 00498 rvoid threadutil_update_lock(jvm_thread_index thridxcurr) 00499 { 00500 /* Only examine INUSE threads in LOCK state */ 00501 if (!(THREAD_STATUS_INUSE & THREAD(thridxcurr).status)) 00502 { 00503 return; 00504 } 00505 00506 switch (THREAD(thridxcurr).this_state) 00507 { 00508 case THREAD_STATE_LOCK: /* Lock arbitration */ 00509 break; 00510 00511 default: /* Not meaningful for this logic */ 00512 return; 00513 } 00514 00515 00516 /* 00517 * One of java.lang.Object.notify() or java.lang.Object.notifyAll() 00518 * or java.lang.Thread.interrupt() or a @c @b synchronize() 00519 * block got the thread here, now try to wake up from @b LOCK with 00520 * the target object's monitor lock acquired, if currently 00521 * available. 00522 * 00523 * To do so, check if previous thread unlocked this object. If so, 00524 * go lock it for use by this thread. 00525 */ 00526 if ((jvm_object_hash_null != THREAD(thridxcurr).locktarget) && 00527 (!(OBJECT_STATUS_MLOCK & OBJECT(THREAD(thridxcurr).locktarget) 00528 .status))) 00529 { 00530 /* If fail, arbitrate here again next time around */ 00531 if (rtrue == 00532 objectutil_synchronize(THREAD(thridxcurr).locktarget, 00533 thridxcurr)) 00534 { 00535 /* Move to @b ACQUIRE state */ 00536 THREAD(thridxcurr).status &= ~THREAD_STATUS_WAIT4EVER; 00537 threadstate_request_acquire(thridxcurr); 00538 } 00539 } 00540 00541 return; 00542 00543 } /* END of threadutil_update_lock() */ 00544 00545 00546 /*! 00547 * @brief Examine an object to see if a thread owns its monitor lock 00548 * 00549 * 00550 * @param thridx Thread table index of thread to compare with. 00551 * 00552 * @param objhashlock Object table hash of object to examine. 00553 * 00554 * 00555 * @returns @link #rtrue rtrue@endlink if this thread owns 00556 * this object's monitor lock. 00557 * 00558 */ 00559 00560 rboolean threadutil_holds_lock(jvm_thread_index thridx, 00561 jvm_object_hash objhashlock) 00562 { 00563 /* 00564 * Make sure thread and object are both in use, 00565 * make sure object is locked, then do @b BIDIRECTIONAL check 00566 * to see if object and thread think each other is in sync 00567 * and that @e this thread indeed @b DOES hold the lock on @e this 00568 * object. 00569 */ 00570 00571 /* Prerequisite to this check */ 00572 if ((!(OBJECT_STATUS_INUSE & OBJECT(objhashlock).status)) || 00573 (OBJECT_STATUS_NULL & OBJECT(objhashlock).status)) 00574 { 00575 return(rfalse); 00576 } 00577 00578 /* Prerequisite to this check */ 00579 if ((!(THREAD_STATUS_INUSE & THREAD(thridx).status)) || 00580 (THREAD_STATUS_NULL & THREAD(thridx).status)) 00581 { 00582 return(rfalse); 00583 } 00584 00585 if ((OBJECT_STATUS_MLOCK & OBJECT(objhashlock).status) && 00586 (thridx == OBJECT(objhashlock).mlock_thridx) && 00587 ((THREAD_STATUS_WAIT4EVER | 00588 THREAD_STATUS_WAITTIMED ) & THREAD(thridx).status) && 00589 (objhashlock == THREAD(thridx).locktarget)) 00590 { 00591 return(rtrue); 00592 } 00593 00594 return(rfalse); 00595 00596 } /* END of threadutil_holds_lock() */ 00597 00598 00599 /* EOF */ 00600