1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.store.raw.xact.RowLocking2 |
4 | |
5 | Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.store.raw.xact; |
22 | |
23 | import org.apache.derby.iapi.services.locks.LockFactory; |
24 | import org.apache.derby.iapi.services.locks.C_LockFactory; |
25 | import org.apache.derby.iapi.services.locks.Latch; |
26 | |
27 | import org.apache.derby.iapi.services.sanity.SanityManager; |
28 | |
29 | import org.apache.derby.iapi.store.raw.ContainerHandle; |
30 | import org.apache.derby.iapi.store.raw.ContainerLock; |
31 | import org.apache.derby.iapi.store.raw.LockingPolicy; |
32 | import org.apache.derby.iapi.store.raw.RecordHandle; |
33 | import org.apache.derby.iapi.store.raw.RowLock; |
34 | import org.apache.derby.iapi.store.raw.Transaction; |
35 | |
36 | import org.apache.derby.iapi.error.StandardException; |
37 | |
38 | |
39 | /** |
40 | A locking policy that implements row level locking with isolation degree 2. |
41 | |
42 | The approach is to place all "write" container and row locks on the |
43 | transaction group lock list. Locks on this group will last until end |
44 | of transaction. All "read" container and row locks will be placed |
45 | on a group list, key'd by the ContainerId of the lock. Locks on this |
46 | list will either be released explicitly by the caller, or will be released |
47 | as a group when the unlockContainer() call is made. |
48 | |
49 | Note that write operations extend from the RowLocking3 implementations. |
50 | |
51 | @see org.apache.derby.iapi.store.raw.LockingPolicy |
52 | */ |
53 | public class RowLocking2 extends RowLockingRR |
54 | { |
55 | // no locking has no state, so it's safe to hold |
56 | // it as a static |
57 | private static final LockingPolicy NO_LOCK = new NoLocking(); |
58 | |
59 | protected RowLocking2(LockFactory lf) |
60 | { |
61 | super(lf); |
62 | } |
63 | |
64 | /** |
65 | * Obtain container level intent lock. |
66 | * <p> |
67 | * This implementation of row locking is 2 level, ie. table and row locking. |
68 | * It will interact correctly with tables opened with ContainerLocking3 |
69 | * locking mode. |
70 | * <p> |
71 | * Updater's will get table level IX locks, and X row locks. |
72 | * <p> |
73 | * Reader's will get table level IS locks, and S row locks. |
74 | * <p> |
75 | * Read locks are put in a separate "group" from the transaction, so that |
76 | * when the container is closed it can release these read locks. |
77 | * |
78 | * @param t Transaction to associate lock with. |
79 | * @param container Container to lock. |
80 | * @param waitForLock Should lock request wait until granted? |
81 | * @param forUpdate Should container be locked for update, or read? |
82 | * |
83 | * @return true if the lock was obtained, false if it wasn't. |
84 | * False should only be returned if the waitForLock policy was set to |
85 | * "false," and the lock was unavailable. |
86 | * |
87 | * @exception StandardException Standard exception policy. |
88 | **/ |
89 | public boolean lockContainer( |
90 | Transaction t, |
91 | ContainerHandle container, |
92 | boolean waitForLock, |
93 | boolean forUpdate) |
94 | throws StandardException |
95 | { |
96 | Object qualifier = forUpdate ? ContainerLock.CIX : ContainerLock.CIS; |
97 | |
98 | // for cursor stability put read locks on a separate lock chain, which |
99 | // will be released when the container is unlocked. |
100 | Object group = |
101 | forUpdate ? ((Object) t) : ((Object) container.getUniqueId()); |
102 | |
103 | boolean gotLock = |
104 | lf.lockObject( |
105 | t.getCompatibilitySpace(), group, container.getId(), qualifier, |
106 | waitForLock ? C_LockFactory.TIMED_WAIT : C_LockFactory.NO_WAIT); |
107 | |
108 | if (gotLock) |
109 | { |
110 | // look for covering table locks |
111 | // CIS and CIX is covered by CX |
112 | // In that case move the lock to the transaction list from the |
113 | // container list, as the null locking policy will do nothing in |
114 | // unlockContainer(). |
115 | // |
116 | |
117 | |
118 | if (lf.isLockHeld(t.getCompatibilitySpace(), t, container.getId(), |
119 | ContainerLock.CX)) |
120 | { |
121 | //release any container group locks becuase CX container lock will cover everthing. |
122 | lf.unlockGroup(t.getCompatibilitySpace(), container.getUniqueId()); |
123 | container.setLockingPolicy(NO_LOCK); |
124 | }else if ((!forUpdate) && |
125 | lf.isLockHeld(t.getCompatibilitySpace(), t, container.getId(), ContainerLock.CS)) |
126 | { |
127 | // move locks from container group to transaction group. |
128 | lf.transfer(t.getCompatibilitySpace(), group, t); |
129 | container.setLockingPolicy(NO_LOCK); |
130 | } |
131 | } |
132 | |
133 | return gotLock; |
134 | } |
135 | |
136 | |
137 | /** |
138 | * Obtain lock on record being read. |
139 | * <p> |
140 | * Assumes that a table level IS has been acquired. Will acquire a Shared |
141 | * or Update lock on the row, depending on the "forUpdate" parameter. |
142 | * <p> |
143 | * Read lock will be placed on separate group from transaction. |
144 | * |
145 | * @param t The transaction to associate the lock with. |
146 | * @param record The record to be locked. |
147 | * @param waitForLock Should lock request wait until granted? |
148 | * @param forUpdate Whether to open for read or write access. |
149 | * |
150 | * @return true if the lock was granted, false if waitForLock was false |
151 | * and the lock could not be granted. |
152 | * |
153 | * @exception StandardException Standard exception policy. |
154 | **/ |
155 | public boolean lockRecordForRead( |
156 | Transaction t, |
157 | ContainerHandle container_handle, |
158 | RecordHandle record, |
159 | boolean waitForLock, |
160 | boolean forUpdate) |
161 | throws StandardException |
162 | { |
163 | Object qualifier = forUpdate ? RowLock.RU2 : RowLock.RS2; |
164 | |
165 | return( |
166 | lf.lockObject( |
167 | t.getCompatibilitySpace(), |
168 | container_handle.getUniqueId(), |
169 | record, |
170 | qualifier, |
171 | waitForLock ? |
172 | C_LockFactory.TIMED_WAIT : C_LockFactory.NO_WAIT)); |
173 | } |
174 | |
175 | /** |
176 | * Obtain lock on record being read while holding a latch. |
177 | * <p> |
178 | * Assumes that a table level IS has been acquired. Will acquire a Shared |
179 | * or Update lock on the row, depending on the "forUpdate" parameter. |
180 | * <p> |
181 | * |
182 | * @param latch The latch being held. |
183 | * @param record The record to be locked. |
184 | * @param forUpdate Whether to open for read or write access. |
185 | * |
186 | * @exception StandardException Standard exception policy. |
187 | **/ |
188 | public void lockRecordForRead( |
189 | Latch latch, |
190 | RecordHandle record, |
191 | boolean forUpdate) |
192 | throws StandardException |
193 | { |
194 | // RESOLVE - Did I do the right thing with the "forUpdate" variable. |
195 | // RESOLVE (mikem) - looks like it needs work for update locks, and |
196 | // compatibility spaces. |
197 | |
198 | Object qualifier = forUpdate ? RowLock.RU2 : RowLock.RS2; |
199 | |
200 | lf.lockObject( |
201 | record.getContainerId(), record, qualifier, |
202 | C_LockFactory.TIMED_WAIT, latch); |
203 | } |
204 | |
205 | public void unlockRecordAfterRead( |
206 | Transaction t, |
207 | ContainerHandle container_handle, |
208 | RecordHandle record, |
209 | boolean forUpdate, |
210 | boolean row_qualified) |
211 | throws StandardException |
212 | { |
213 | Object qualifier = forUpdate ? RowLock.RU2 : RowLock.RS2; |
214 | |
215 | int count = |
216 | lf.unlock( |
217 | t.getCompatibilitySpace(), container_handle.getUniqueId(), |
218 | record, qualifier); |
219 | |
220 | if (SanityManager.DEBUG) |
221 | { |
222 | // in the case of lock escalation the count could be 0. |
223 | if (!(count == 1 || count == 0)) |
224 | { |
225 | SanityManager.THROWASSERT( |
226 | "count = " + count + |
227 | "record.getContainerId() = " + record.getContainerId()); |
228 | } |
229 | } |
230 | } |
231 | |
232 | /** |
233 | * Unlock read locks. |
234 | * <p> |
235 | * In Cursor stability release all read locks obtained. unlockContainer() |
236 | * will be called when the container is closed. |
237 | * <p> |
238 | * |
239 | * @param t The transaction to associate the lock with. |
240 | * @param container_handle Container to unlock. |
241 | **/ |
242 | public void unlockContainer( |
243 | Transaction t, |
244 | ContainerHandle container_handle) |
245 | { |
246 | // Only release read locks before end of transaction in level 2. |
247 | lf.unlockGroup( |
248 | t.getCompatibilitySpace(), container_handle.getUniqueId()); |
249 | } |
250 | } |