Mechanism for managing the locking and unlocking of objects in Java6411983Abstract A mechanism for managing the locking and unlocking of objects. The mechanism comprises a transition vector having an ordered set of transition elements. Each of the transition elements includes a locking function or a pointer to the locking function and an unlocking function or a pointer to the unlocking function. When an object is created a transition vector is assigned to the object. The object includes a reference element which references a transition element in the vector. In response to a request to lock the object, the locking function in the transition element is invoked and the next transition element is assigned to the object and the reference element in the object is updated. For a request to unlock the object, the unlocking function in the transition element is invoked and the previous transition element is assigned to the object and the reference element is updated. The mechanism improves execution overhead by providing an implicit count for determining the lock count on an object. The mechanism also features typed transition vectors for structured locking/unlocking and unstructured locking/unlocking operations. Claims The embodiments of the invention in which an exclusive property or privilege is claimed are defined as follows: Description FIELD OF THE INVENTION
The initial locking routine lock-0():
1: result = compareAndSwap(&mte, &e0, _Anchor( ))
2: if (result == TRUE)
3: then
3a: the locking process terminates
4: else
4a: the wait_list of the object is updated, if necessary,
with a non-zero value to indicate that a thread is
waiting to lock the object
4b: the thread waits as necessary until it can obtain a
lock on
the object
The shallow locking routines lock-1( ) to lock-N-1( ) :
1: result = currentThreadOwnsLock(o)
2: if (result == TRUE)
3: then
3a: increment the mte field of the object by 12
3b: the locking process terminates
4: else
4a: call lock-0( ) to lock the object
4b: the locking process terminates when lock-0( ) completes
The shallow locking routine lock-N( ):
1: result = currentThreadOwnsLock(o)
2: if (result == TRUE)
3: then
3a: increment the mte field of the object by 12
3b: search the d_anchor list for a free entry, or allocate
one ;
call this entry M
3c: place M at the front of the d_anchor list
3d: set the deep_lock count in M to 1
3e: set the &OBJECT-i field in M to the object reference
3f: the locking process terminates
4: else
4a: call lock-0( ) to lock the object
4b the locking process terminates when lock-0( ) completes
The deep locking routine lock-Deep( ):
1: result = currentThreadOwnsLock(o)
2: if (result == TRUE)
3: then
3a: M = the first object at the head of the d_anchor list
3b: F = &OBJECT-i field of M
3c: if (F is NOT the object to be unlocked)
3d: then
3e: M = the entry in the d_anchor list with an
&OBJECT-i
field equal to the object to be unlocked
3f: M is moved to the front of the d_anchor list
3g: increment the deep_lock count in M by 1
3h: the locking process terminates
4: else
4a: call lock-C( ) to lock the object
4b: the locking process terminates when lock-0( ) completes
The initial locking routine lock-0( ) utilizes a "compare and swap" function shown in Line 1 of the pseudocode listing. The compare and swap function provides a mechanism to manage contention. The function atomically compares the four byte memory location &mte referenced by the monitor transition element field 23 to the referenced address &e0 for equality, and assigns a value to the pointer t_Anchor( ) if the comparison succeeds, i.e. the pointer t_Anchor( ) is equal to location of the first transition element 32a or &e1(tid). The function returns a Boolean value result based on the success of the comparison (Line 1) which is used to determine whether the current thread owns a lock on the object (Line 2). If the result of the compare and swap operation is TRUE (Line 3), then the swap has succeeded and the locking process terminates (Line 3a). If the Boolean result is FALSE (Line 2), then another thread has a lock on the object 22 (Line 4). Accordingly, the thread updates the wait_list field 24 of the object 22 (Line 4a) and waits until it can obtain the initial lock function lock-0( ) for the object 22 (Line 4b). To indicate that a thread is waiting to lock the object 22, the wait_list field 24 is updated as necessary with a non-zero value. As will be understood by those skilled in the art, the compare and swap function can be implemented on a number of hardware platforms using machine instructions. For the Intel hardware platform, the LOCK CMPXCHG instruction is used in a multi-processor environment and the CMPXCHG instruction is used in a uni-processor environment. Reference is next made to the pseudocode listings for the shallow locking routines lock-1( ) to lock-N-1( ). The shallow locking routines lock-1( ) to lock-N-1( ) utilize a function currentThreadOwnsLock(o) as shown in Line 1. The function returns a Boolean value result (Line 1) which is used to determine whether the current thread owns a lock on the object. If the Boolean result is TRUE (Line 2), then the current thread owns a lock on the object (Line 3). Accordingly, the monitor transition element field 23 is incremented by 12 to point to the next monitor transition element 34 in the vector 30 (Line 3a), and the locking process is terminated (Line 3b). It will be appreciated that the 12 corresponds to the 12 byte size (i.e. 4 bytes for each field 34, 36, 38 in FIG. 2) of a monitor transition element 32. If the Boolean result is FALSE (Line 2), then the current thread does not own a lock on the object (Line 4). Accordingly, the initial locking function lock-0( ) or 34a is called in order to lock the object (Line 4a), and the locking process terminates when the function lock-0( ) completes (Line 4b). The currentThreadOwnsLock( ) function may be implemented as a function which compares the identifier of the current thread, obtained in a platform dependant manner, with the value tid. The value tid is indirectly accessible from monitor transition element field 23 of the object 22. On platforms where t_anchor is used to double as both the thread ID and the address of Per thread data, a faster alternative implementation is possible. A tailored version of the currentThreadOwnsLock( ) function may be provided for each of the locking functions lock-K( ) by observing that the value for monitor transition element field 23 is t_anchor+12(K-1) for the thread owning the lock on the object. Reference is next made to the pseudocode listing for the shallow locking routine lock-N( ). The shallow locking routine lock-N( ) also utilizes the function currentThreadOwnsLock(o) (Line 1). The function returns a Boolean value result (Line 1) which is used to determine whether the current thread owns a lock on the object (Line 2). If Boolean result is TRUE (Line 2), then the current thread has a lock on the object 22 (Line 3), and the following operations are performed. The monitor transition element field 23 in the object 22 is incremented by 12 to point to the element eDeep or 32m in the monitor transition vector 30 (Line 3a). Next, the linked list 40 or d_anchor is searched for a free entry (Line 3b). If a free entry is not found, then an entry is allocated. The entry is named "M" and moved to the front of the linked list 40 or d_anchor (Line 3c). Next, the deep lock count field 45 is set to 1 (Line 3d) and the field 44 or &OBJECT-i is set to the object reference (Line 3e). The locking process is now complete and terminates (Line 3f). If the Boolean result( is FALSE (Line 2), then another thread has a lock on the object (Line 4). Accordingly, the process for initially locking the object is initiated (Line 4a) by calling the initial locking routine lock-0( ) as described above. The locking process is terminated when the initial locking routine lock-0( ) has completed (Line 4b). Reference is next made to the pseudocode listing associated with the deep locking routine lock-Deep( ). The deep locking routine lock-Deep( ) utilizes the function currentThreadOwnsLock( ). The function returns a Boolean value result (Line 1) which is used to determine whether the current thread owns a lock on the object 22 (Line 2). If the Boolean value return is TRUE (Line 2), then the current thread has a lock on the object (Line 3). Next, the routine lock-Deep( ) checks if the first object (i.e. "M" in Line 3a) at the head of the linked list 40 or d_anchor is the current object by comparing the value at &OBJECT-i (i.e. "F" in Line 3b) with the current object reference (Line 3c). If the objects do not match (Line 3d), the linked list d_anchor is searched for an &OBJECT-i field equal to object to be unlocked (Line 3e). This search should always succeed. The located object, i.e. "M", is then moved to the head of the list d_anchor (Line 3f). The deep locking routine then increments the value in the deep lock count field 45 for the entry at the head of the d_anchor list (Line 3g), and the locking process is terminated (Line 3h). If the first object in the linked list 40 is the current object (Line 3c), the deep lock count is incremented by 1 (Line 3g) and the locking process terminates (Line 3h). If the Boolean result is FALSE (Line 4), then another thread has a lock on the object, and the process for the initial locking routine lock-0( ) is initiated to lock the object (Line 4a). The initial locking routine lock-0( ) is described above. The locking process terminates once the locking routine lock-0( ) has completed (Line 4b). The locking routines for unstructured locking are implemented in the same fashion as the structured locking routines described above. The implementation of the structured unlocking routines will now be described with reference to the following pseudocode listings.
The invalid unlocking routine unlock-0( ):
1: throw an exception.
The initial unlocking routine unlock-1( ):
0: result = currentThreadOwnsLock(o)
0a: if (result == FALSE)
0b: then
0c: throw an exception.
1: assign &e0 to the mte field of the object.
2: if (wait_list field of object is 0)
3: then
3a: the unlocking process terminates
4: else
4a: a thread is awakened where it can contend for the lock
4b: the unlocking process terminates
The shallow unlocking routines unlock-2( )-unlockN( ):
0: result currentThreadOwnsLock(o)
0a: if (result == FALSE)
0b: then
0c: throw an exception
1: decrement the mte field of the object by 12.
2: The unlocking process terminates
The deep unlocking routine unlock-Deep( ):
0: result = currentThreadOwnsLock(o):
0a: if (result == FALSE)
0b: then
0c: throw an exception
1: M = the first object at the head of the d_anchor list
2: F = the value of the &OBJECT-i of M
3: if (F is NOT the object to be be unlocked
4: then
4a: M = the entry in the d_anchor list with an &OBJECT-i
fiel d
whose value is equal to the object to be unlocked
4b: move M to the head of the d_anchor list
5: decrement the deep_lock count in M by 1.
6: if (deep lock count is not 0)
7: then
7a: the unlocking process terminates
8: else
8a: set the &OBJECT-i field of M to zero
8b: decrement the mte field of the object by 12.
8c: the unlocking process terminates
In the pseudocode listing shown above, additional steps denoted by Lines 0, 0a, 0b, 0c are provided for unstructured locking as will also be described below. It will be understood that an implementation of structured and unstructured locking requires two sets of monitor transition vectors. Reference is first made to the pseudocode listing for the invalid unlocking routine unlock-0( ). As shown, the initial routine unlock-0( ) throws an exception if invoked by a thread (Line 1). The exception is generated because the current thread (or any other thread) does not yet have a lock on the object. Reference is next made to pseudocode listing for the unlocking routine unlock-1( ). The first step in the unlocking routine unlock-1( ) (Line 1) involves updating the monitor transition element field 23 so that another thread can lock the object at this time. The next step (Line 2) involves checking the wait_list field 24 in the object 22 to determine if there any other threads waiting to lock the object 22. If there are no threads waiting to lock the object (Line 3), then the unlocking operation is complete and the routine terminates (Line 3a). 0n the other hand, if one or more threads are waiting (Line 4), then a thread is awakened so that the thread can contend for the lock (Line 4a). The thread contends with any other threads attempting to also lock the object. The unlocking routine then terminates (Line 4b). If the unlocking operation is unstructured, then the unlocking routine unlock-1( ) for unstructured locking includes the additional steps at Lines 0, 0a, 0b and 0c. The purpose of these additional steps is to determine if the current thread has a lock on the object. Unlocking the routine unlock-1( ) utilizes the function currentThreadOwnsLock(o) (Line 0). The function returns a Boolean value result which is used to determine whether the current thread owns a lock on the object (Line 0a). If the Boolean result is FALSE (Line 0b), then the current thread does not own a lock on the object, and accordingly an exception is thrown (Line 0c). Reference is next made to the pseudocode listing for the shallow unlocking routines unlock-2( )-unlock-N( ). The shallow unlocking routines include an operation to update the monitor transition element field 23 to the previous element 32 in the monitor transition vector 30. This operation involves decrementing the value stored in the transition element field 23 by 12 (Line 1). For the embodiment shown in FIG. 2 and described above, each transition element 32 in the vector 30 comprises 12 bytes. Once the value is decremented, the unlocking routine terminates (Line 2). If the unlocking operation is unstructured, then the shallow unlocking routines unlock-2( )-unlock-N( ) include the additional steps at Lines 0, 0a, 0b and 0c. As shown, the routine utilizes the function currentThreadOwnsLock(o) (Line 0). The function returns a Boolean value result (Line 0) which is used to determine whether the current thread owns a lock on the object (Line 0a). If the Boolean result is FALSE (Line 0b), then the current thread does not own a lock on the object, and accordingly an exception is thrown (Line 0c). Reference is next made to the pseudocode listing for the deep unlocking routine unlock-Deep( ). The deep unlocking routine unlock-Deep( ) first determines if the object at the head of the linked list d_anchor is the current object. This operation involves assigning the first object in the list to the variable "M" (Line 1) and then assigning the address of the object (obtained from M) to the variable "F" (Line 2). Next, the object reference, i.e. F, is compared to the current object reference (Line 3). If the current object is not at the top of the linked list d_anchor (Line 4), then a search is made for the entry in the linked list d_anchor corresponding to the current object to be unlocked (Line 4a). Next, the located entry, i.e. "M", is moved to the head of the d_anchor list (Line 4b). Next, the deep_lock count in "M" is decremented by 1 (Line 5). It will be appreciated that this decrement is done for the path where M is moved to the front of the list d_anchor and also for the path where M is not moved to the front of the list, i.e. the object being unlocked corresponds to the current object. Next, the decremented deep_lock count is compared to zero (Line 6). If the deep_lock count 45 is not zero, a deep lock still exists (Line 7) and the unlocking process terminates (Line 7a). If the deep_lock count is zero (Line 8), then a regression to shallow locking is required. To perform this operation, the object reference field 44 in the monitor control block 42 is set to zero (Line 8a). Next, the monitor transition element field 23 of the object 22 is decremented by 12 (Line 8a), i.e. the value in the transition element field 23 is set to point to the last shallow locking element 32n or eN(tid). The unlocking process then terminates (Line 8c). If the unlocking operation is unstructured, then the deep unlocking routine unlock-Deep( ) includes the additional steps at Lines 0, 0a, 0b and 0c. The routine unlock-Deep( ) utilizes the function currentThreadOwnsLock(o) as shown in Line 0. The function returns a Boolean value result (Line 0) which is used to determine whether the current thread owns a lock on the object (Line 0a). If the Boolean result is FALSE (Line 0b), then the current thread does not own a lock on the object, and accordingly an exception is thrown (Line 0c). To improve the operation of the deep locking routine lock-Deep( ), two enhancements may be made. First, the monitor control blocks 42 for the d_anchor list are allocated out of a thread specific pool. Second, free entries in the list d_anchor, except the initial one assigned to list, are returned to this thread specific pool. To provide unstructured locking, two monitor transition vectors 50, 60 are required. As shown in FIG. 3, each of the vectors 50, 60 has the same basic structure of the vector 30 described above. The monitor transition vectors 50, 60 include a first monitor transition element 51, 61 (e0) which resides in global storage and remaining monitor transition elements 52, 62 (e1-eN and eDeep) which reside in storage local to the thread. The respective monitor transition elements 52, 64 are shown individually as 52a, . . . , 52n, 52m and as 62a, . . . , 62n, 62m in FIG. 3. Each of the monitor transition elements 51, 52 for the structured vector 50 comprises a structure having two function pointers: &lock 54 and &unlock 56. The transition elements 51, 52 include an additional field 58 for storing a thread identifier or lid. Similarly, each of the monitor transition elements 61, 62 for the unstructured vector 60 comprises a structure having two function pointers: &Ulock 64 and &Unlock 66. The transition elements 61, 62 include an additional field 68 for storing a thread identifier or tid. To simplify the casting operation as described below, the vectors 50, 60 have the same number of elements 52, 62 and vDelta is the byte offset between the associated elements in the different vectors 50, 60. There are however two primary differences between the structured monitor transition vector 50 and the unstructured monitor transition vector 60. First, the unlocking routines 56 in the structured vector 50 do not include a lock ownership test (i.e. currentThreadOwnsLock(o) described above) while this test is performed for the unstructured vector 60. It will be understood that the test is not necessary in the structured case as lock ownership is guaranteed. Second, the structured vector 50 includes its own initial element 51 or e0 which is a global, and the unstructured vector 60 also includes its own initial element 61, which is also a global. It will be appreciated that the initial elements 51, 61 (i.e. e0) are in global storage and are not thread specific since the addresses of the elements 51, 61 are assigned to the monitor transition element field of the object when the object is created and when the object is eventually unlocked by the unlocking function for lock-1( ). In other words, the monitor transition element field of an unlocked object has no associated thread specific locking data. An implementation consideration is that a thread references the monitor transition elements, i.e. e1(tid)-eN(tid),eDeep(tid), of a monitor transition vector of another thread. As described above, the monitor transition elements e1(tid)-eN(tid),eDeep(tid) comprise the "Per thread data" for the particular thread. In order to handle the situation where the thread containing these elements terminates before the reference from the other thread is complete, the storage of Per thread data which contains the structured locking vector is never de-allocated but is recycled, i.e. added to a recycling bin. When added to the recycling bin, the function pointers in the vector are left intact but the lid field is set to an invalid thread ID. The former is necessary in order for an algorithm to properly work. Support for cross thread casting gives rise to additional considerations as will be described below. According to this embodiment, casting is implemented as an operation which may update the monitor transition element field 23 of the object 22 to point to an element 62 in the unstructured vector 60. In the following description, the casting operation is performed between the elements 52, 62 (i.e. e1-eN or eDeep). The initial elements 51, 61 (i.e. e0) are not involved. In the application of cross thread casting described below, where casting is used to eliminate the wait_list field of the object, the casting operation involves additional sets of transition vectors. The implementation of the casting operation will now be described with reference to the following pseudocode listing.
cast (castType, cast)
1: element = monitor transition element field of the object
2: sourceType = the type of the object
3: if (sourceType == castType)
3a: then
3b: the casting operation is complete
4: else
4a: Y = address of the appropriate monitor transition
element of the new type
4b: assign Y to the monitor transition element field
of the object
4c: the casting operation is complete.
The first step in the cast routine involves referencing the monitor transition element field 23 in the object 22 (Line 1). Next, the sourceType is determined from the type of the element (Line 2). The sourceType is then compared to the castType (Line 3). If the castType and sourceType are the same (Line 3a), then there is nothing to do because the object is already of the appropriate type, i.e. the casting operation is complete (Line 3b). If the castType and the sourceType are different (Line 4), then the address of the associated monitor transition element 52 or 62 in the new vector 50 or 60 type needs to be determined (Line 4a). Referring to FIG. 3, if casting from a structured locking operation to an unstructured locking operation, then vDelta is added to the address of the current monitor transition element 52 to obtain the corresponding transition element 62 in the unstructured vector 60. The address of the new transition element 62 is then assigned to the object 22 (Line 4b), and the casting operation is complete (Line 4c). In order to accommodate the situation where a thread is trying to acquire a first time lock on a monitor it believes to be of type X (e.g. structured), but the type has been changed to Y (e.g. unstructured), an additional check is included. When the lock stealing code (i.e. cornpareAndSwap) fails, the thread checks if the type of the monitor has changed. If the type has changed, then the thread invokes the lock routine that is currently associated with the monitor. The need for the wait list field 24 in the object 22 may be eliminated by utilizing the "cross thread casting" feature to manage contention as follows. Two additional types, i.e. monitor transition vectors, are provided: (1) a structured vector with "waiters" type, and (2) an unstructured vector with "waiters" type. When a thread fails to acquire a lock because the lock is held by another thread, the object is cross thread cast to one of the new vector types, i.e. structured waiters vector or unstructured waiters vector. Casting is implemented using the compareandSwap function. When the thread which owns the lock attempts to release the lock (i.e. by assigning the initial element 51, 61 or e0 in FIG. 3 to the monitor transition element field of the object) using the function compareAndSwap, the compareAndSwap may fail. A failure to release the lock indicates that another thread has performed the cast operation. In response to the failure to release the lock, the thread owning the lock releases it appropriately and then gives the waiting thread(s) the proper attention. When the thread that was waiting, or another thread for that matter, gains control of the lock, the casted type remains the same and is not changed back. The type could remain the same for some time since there could be a queue of waiters by this time. When the queue of waiters is empty and the last waiter is about to release the lock, it notices that there are no other waiting threads and, at this time, casts the type back to one without waiters. It will be appreciated that with this approach, all updates to the monitor transition element field of the object are performed using the compareAndSwap function. This update is performed both by the thread which owns the lock and by threads contending for the lock, the code for the locking and unlocking needs to be suitably modified to take this into account. Since updates to the monitor transition element field of the object are performed by nested locking and unlocking routines, this particular utilization of the compareAndSwap function will impact nested locking performance. While cross thread casting will impact nested locking performance, "cross thread plumbing" does not. Cross thread plumbing may also be used to manage contention and thereby eliminate the need for the wait list field 24 in the object 22. When a thread is unable to acquire a lock because the lock is held by another thread, the thread posts a request on the thread owning the lock. The posted request indicates that there is some thread waiting on a lock held by the thread with the post. The posting can be implemented by using cross thread plumbing to change the function pointer of the structured unlock routine, i.e. unlock-1( ), in the monitor transition vector to a different value. The fact that the function pointer has a value different from its default is an indication of the post. For that matter, any location in Per thread data can be used and it need not be a location in a monitor transition vector. It will be understood that in a sense, this field replaces the wait_list field of the object, with the exception that this field could be modified for any or all objects owned by that thread and not just for a particular object. In another aspect, the mechanism 1 according to the invention allows for the reduction of synchronization overhead in applications which are single threaded. The technique is as follows. When the virtual machine is started, the initial locking and unlocking routines for structured locking do not need to consider contention for the initial thread. The "compare and swap" function described above for initially grabbing a lock on an object may be replaced by a store operation into the monitor transition element field of the object. Similarly, the nested lock routines do not need to perform lock ownership or wait_list contention tests. When a second thread is created, the locking routines for the single thread are replaced by routines which consider contention as described above. The plumbing operation is utilized to replace the locking routines prior to the creation of the second thread. In the first embodiment which uses thread specific storage, global and thread specific plumbing techniques are used as described above. In the second embodiment described below, the global plumbing operation is used. It will however be appreciated by those skilled in the art that this technique is not entirely suitable for a virtual machine environment where the second thread is attached or created by a thread other than the initial thread. The mechanism for distinguishing structured and unstructured locking also provides a means for efficiently tracking monitors. Reference is made to FIG. 4 which shows data structures according to this aspect of the invention. The data structures comprise a structured locking stack 70 and an unstructured locking stack 80. The stacks 70, 80 allow locks acquired by structured locking operations to be tracked in one manner and locks acquired by unstructured locking operations to be tracked in another manner. As shown in FIG. 4, the structured locking stack 70 comprises a series of locations 72, which are shown individually as 72a, 72b, . . . , 72n. When a structured lock is performed on an object, a reference &OBJECT-X to the object is pushed onto the next location 72 in the structured stack 70. The next available location 72 in the stack 70 for an object reference &OBJECT-X is indicated by a stack pointer 74 or struct-stkp. The pointer 74 is stored in a portion of the per thread data, and is updated after an object reference &OBJECT-X is pushed onto the stack 70. The top and end of the stack 70 are marked by pointers 76 or struct-tos and 78 or struct-eos, respectively. These pointers 76, 78 are modified if the stack 70 is allowed to grow. The unstructured locking stack 80 has a similar organization with locations 82, pointer 84 or unstruct-stkp, pointer 86 or unstruct-tos, and pointer 88 or unstruct-eos. In operation, an object that is locked by a thread is pushed onto either the structured locking stack 70 or onto the unstructured locking stack 80. The object (i.e. the pointer to the object) is pushed onto the appropriate stack during an initial lock operation. An object is popped from the appropriate stack 70 or 80 during the last unlock operation. During a casting operation, an object will be moved from the structured locking stack 70 to the unstructured locking stack 80 when the type changes from structured to unstructured. The "push" and "pop" operations for the structured locking stack 70 can be done efficiently since only the top entry ever needs to be considered. The stack pointer struct-stkp and the other pointers 76, 78 are at a constant displacement from the monitor transition element 52 referenced by the value in the monitor transition element field 23 stored in the object 22. As such, the object references in the stack 70 may be retrieved using one level of indirection. As such there is no need for special checking code, except possibly, for stack overflow on a push operation. In a second embodiment of a mechanism 100 according to the present invention, the monitor transition vector(s) are stored in global memory. As shown in FIG. 5, the mechanism 100 includes one or more monitor transition vectors 110. The organization of the monitor transition vector 110 is very similar to the monitor transition vector 10 described above, except the vector 110 according to this embodiment is stored in global memory. The monitor transition vector 110 is referenced by an object 102. The object 102 includes a monitor transition element field 104 and a thread identifier field 106. The monitor transition vector 110 comprises an ordered set of monitor transition elements 112, shown individually as 112a, 112b, 112c, . . . , 112n, 112m. When the object 102 is initially created, the monitor transition element field 104 in the object 102 stores the address or a pointer to the first monitor transition element 112a or e0 in the monitor transition vector 110. During subsequent lock and unlock operations, the monitor transition element 112 currently assigned to the object 102 is updated. As described above, each monitor transition element 112 is implemented as a structure containing two function pointers: &lock 114 and &unlock 116. The &lock pointers 114, shown individually as 114a, 114b, 114c, . . . , 114n, 114m, each reference a locking transition function lock-K( ), whereas the &unlock pointers, shown individually as 116a, 116b, 116c, . . . , 116n, 116m, each reference an unlocking transition function unlock-K( ). Referring to FIG. 5, the locking functions lock-0( ) to lock-Deep( ) form a set of N+2 functions used for structured locking of an object 102. The initial locking function lock-0( ) is used to acquire the first lock on the object 102. The locking functions lock-1( ) to lock-N( ) are used to acquire the next N shallow locks on the object 102. The deep locking function lock-Deep( ) is used to acquire all further, i.e. deep, locks on the object 102. Similarly, the unlocking functions unlock-0( ) to unlock-Deep( ) form a set of N+2 functions used for structured unlocking of the object 102. The initial unlocking function unlock-0( ) merely generates an exception because it is illegal to unlock an object that is not locked. The unlocking functions unlock-1( ) to unlock-N( ) are used to unlock an object 102 that has a lock count from 1-N respectively. The deep unlocking function unlock-Deep( ) is used to unlock an object 102 that has a deep_lock count. While, the monitor transition vector 110 is kept in global memory, the mechanism 100 includes thread specific storage 118 for data that is unique to each thread. As shown in FIG. 5, a linked list d_anchor is maintained in the thread specific storage 118. The base address of the thread specific storage 118 is given by a pointer t_anchor. As described above, a function t_Anchor( ) is used by a thread to determine its t_anchor value. The function t_Anchor( ) is also required by the deep lock routines (i.e. lock-Deep( ) and unlock-Deep( ) when allocating or updating a monitor control block 120. The linked list d_anchor references a list of monitor control blocks 120 (shown individually as 120a, 120b, . . . ) or MCB's which are used by the deep locking lock-Deep( ) and unlocking unlock-Deep( ) routines. The monitor control blocks are also kept in thread specific storage 118, i.e. Per thread data. The list d_anchor includes an entry for each object that is deeply locked by the thread. As shown in FIG. 5, the monitor control block 120 comprises a next link field 122, an object reference field 124, and a deep lock count field 126. The next link field 122 holds the address of the next monitor control block 120b in the list. The object reference field 124 stores an object reference &OBJECT-i for the deeply locked object. A zero value in object reference field 124 indicates that the monitor control block 120 is not being used. The deep lock count field 126 stores a value which indicates the number of calls to the deep locking routine lock-Deep( ). A non-zero value of M indicates there have been M-1 deep lock requests. In a typical 32-bit environment, the monitor transition element 104 comprises a 4 byte field and the thread identifier field 106 also comprises a 4 byte field. This represents the storage requirement in the object 102 for synchronization. When an object 102 is created, the monitor transition element field 104 is assigned the address of the first monitor transition element 112a or e0 in the vector 110. The monitor transition element field 104 is subsequently updated by the locking lock-K( ) and unlocking unlock-K( ) routines and by the casting operation. At any point in time, the monitor transition element field 104 will contain the address of one of the locking routines 114 lock-K( ) or lock-Deep( ). In this embodiment, the thread identifier tid is stored in a field 106 in the object 102. The field 106 holds the identifier of the thread owning a lock on the object 102. It will be appreciated that the mechanism 100 according to this embodiment does not include a wait_list field 24 (FIG. 3). Accordingly, wait_list management for contention control is performed using the cross thread casting technique as described above. This means that a compareAndSwap operation is needed for each store into the monitor transition element field 104 of the object 102. If a wait_list field is added to the object, compareAndSwap usage can be minimized. It will be appreciated that the cross thread plumbing operation is not supported because all plumbing is global. For the first embodiment described above which exploits thread specific storage, the "Per thread data" needed to be recycled since portions of the monitor transition vectors 50, 60 (FIG. 3) were kept in thread specific data. For the mechanism 100 shown in FIG. 5, this requirement is eliminated since the transition vectors 110 are kept in global storage. In order to allow tracking of the objects locked by a given thread as described above for the first embodiment, the value t_anchor is required by the initial locking lock-0 and unlocking unlock-0 routines. The present invention may be embodied in other specific forms without departing from the spirit or essential characteristics thereof. Therefore, the presently discussed embodiments are considered to be illustrative and not restrictive, the scope of the invention being indicated by the appended claims rather than the foregoing description, and all changes which come within the meaning and range of equivalency of the claims are therefore intended to be embraced therein.
|
Same subclass Same class Consider this |
||||||||||
