1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.river.outrigger; 19 20 import net.jini.io.MarshalledInstance; 21 import org.apache.river.landlord.LeasedResource; 22 import org.apache.river.outrigger.proxy.EntryRep; 23 import java.util.Queue; 24 25 /** 26 * This object holds an annotated reference to an 27 * <code>EntryRep</code> object. Currently there is one annotation, 28 * which is a hash code for the object that can be used as a 29 * quick-reject comparison when scanning through the list. The handle 30 * holds a hash code that is based on the bytes that encode the first 31 * <i>N</i> fields, where <i>N</i> is the number of fields in the 32 * entry up to a maximum (currently this maximum is 16 (64 bits 33 * divided by 4 bits/field), so that 4 is the minimum number of bits 34 * per field in the hash). 35 * <p> 36 * When comparing, the template's own hash is calculated, and also a 37 * mask that masks out the hash codes of wildcard fields. A template 38 * will match an entry only if the entry's EntryHandle hash masked with 39 * the template's wildcard mask is the same as the template's hash. 40 * <p> 41 * Care must be taken since the template may be a supertype of the type 42 * being searched. This is why the number of fields in the static 43 * methods is passed as an argument, not simply taken from the entry in 44 * question. When a template's hash is being created, its hash value 45 * is calculated as if it were of the class being searched, with the 46 * subclass's field count. Any extra fields are assumed to be 47 * wildcards. This means that the template's hash must be recalculated 48 * for each subclass it is compared against, but this only happens once 49 * per known subclass, and so is probably not onerous. <p> 50 * 51 * There is a particular risk with the removal of entries outside of 52 * transactions. Ideally marking an entry as removed and making the 53 * removal durable would be atomic with respect to other 54 * operations. But this would require holding a lock across disk I/O 55 * which we try to avoid. In particular it would hold up the progress 56 * of searches that match the entry in question, even though the very 57 * next entry might be a suitable match. One alternative would be to 58 * make the removal durable, then while holding the entry's lock mark 59 * the entry as removed, but this would allow competing takes to both 60 * get the same entry (this could be corrected by making the 2nd take 61 * lose when it goes back to try and complete the removal, and then 62 * continues its query, but since logging happens up in 63 * OutriggerServerImpl restarting the query would be inconvenient, it 64 * would probably also result in a number of unnecessary log 65 * records). We could mark the entry as removed, release the entry's 66 * lock, and then make the removal durable. However, this allows for 67 * the possibility of a 2nd query that matches the entry coming in 68 * after the entry has been removed, but before the removal has been 69 * made durable, finding no matches and returning null, and then the 70 * server crashing before the removal is made durable. When the server 71 * came back up the entry would be available again, and if the 2nd 72 * query was repeated it could then return the entry that had been 73 * marked as removed. Effectively an entry would have disappeared and 74 * then reappeared. <p> 75 * 76 * Our solution is to introduce the <i>removePending</i> flag. When an 77 * entry is to be removed outside of a transaction the removePending 78 * flag is set by calling <code>provisionallyRemove</code>, the 79 * removal is made durable, the entry is removed internally and the 80 * removePending flag cleared (generally by calling 81 * <code>remove</code> on the appropriate <code>EntryHolder</code> or 82 * on the <code>EntryHolderSet</code> - either will remove the entry 83 * from all the internal tables and clear the removePending flag). <p> 84 * 85 * Any operation that will definitively indicate that a given entry 86 * has been removed must not only check to see if the entry has been 87 * removed but also that removePending is not set (the 88 * <code>isProvisionallyRemoved</code> method returns the state of the 89 * removePending flag). If removePending is set the operation must 90 * either block until removePending is cleared (this can be 91 * accomplished using the <code>waitOnCompleteRemoval</code> method), 92 * indicating that the removal has been made durable, or return in 93 * such a way that the entry's state is left ambiguous. Note, because 94 * any I/O failure while logging will result in the space crashing a 95 * set removePending flag will only transition to cleared after a 96 * removal has been made durable, thus an operation blocked on the 97 * removePending flag should never need to go back and see if the 98 * entry has become available. <p> 99 * 100 * Note some of the method of this class are synchronized internally, 101 * while other are synchronized externally. Methods which need to be 102 * synchronized externally are called out in their comments. 103 * 104 * @author Sun Microsystems, Inc. 105 */ 106 // We do not store this data on the EntryRep object itself because it 107 // is not really part of the client<->JavaSpaces service protocol -- 108 // some implementations of EntryHolder may not choose to use this 109 // mechanism. It does add an extra object per EntryRep object in 110 // those that *do* use it, and so we may want to re-examine this in the 111 // future. 112 113 class EntryHandle extends BaseHandle implements LeaseDesc, Transactable { 114 /** the content hash for the rep */ 115 private final long hash; // Made final for toString() and hash(). 116 117 /** 118 * If this entry is locked by one or more transaction the info 119 * on those transactions, otherwise <code>null</code>. 120 */ 121 private TxnState txnState; 122 123 /** 124 * <code>true</code> if this entry has to been seen as removed, 125 * but the removal has not yet been committed to disk 126 */ 127 private boolean removePending = false; 128 private boolean removed = false; 129 130 /** 131 * Create a new handle, calculating the hash for the object. 132 * If <code>mgr</code> is non-<code>null</code> start the entry 133 * as write locked under the given transaction. 134 * @param rep The rep of the entry this is a handle for 135 * @param mgr If this entry is being written under a transaction the 136 * manager for that transaction, otherwise <code>null</code> 137 * @param holder If mgr is non-<code>null</code> this must be 138 * the holder holding this handle. Otherwise it may be 139 * <code>null</code> 140 * @param content Queue this EntryHandle will be removed from. 141 */ 142 EntryHandle(EntryRep rep, TransactableMgr mgr, EntryHolder holder, Queue<EntryHandle> content) { 143 super(rep, content); 144 hash = (rep != null ? hashFor(rep, rep.numFields())[0] : -1); 145 if (mgr == null) { 146 txnState = null; 147 } else { 148 if (holder == null) 149 throw new NullPointerException("EntryHandle:If mgr is " + 150 "non-null holder must be non-null"); 151 txnState = new TxnState(mgr, TransactableMgr.WRITE, holder); 152 } 153 } 154 155 // inherit doc comment 156 public LeasedResource getLeasedResource() { 157 return rep(); 158 } 159 160 /** 161 * Return this handle's content hash. 162 */ 163 long hash() { 164 return hash; 165 } 166 167 /** 168 * Calculate the hash for a particular entry, assuming the given 169 * number of fields, filling in the fields of <code>desc</code> 170 * with the relevant values. <code>desc</code> may be 171 * <code>null</code>. <code>numFields</code> must be >= the number 172 * of fields in the <code>rep</code> object (this is not 173 * checked). 174 * 175 * @see #hashFor(EntryRep,int) 176 * @see #descFor(EntryRep,int) 177 * @return long[4] containing the hash, bitsPerField, fieldsInHash and mask 178 * in that order. 179 */ 180 private static long [] 181 hashFor(EntryRep rep, int numFields) 182 { 183 long [] result = new long [4]; 184 if (rep == null || numFields == 0) return result; 185 186 /** Number of bits allocated in the hash for each field */ 187 int bitsPerField = Math.max(64 / numFields, 4); // at least 4 bits 188 /** How many fields are used in the hash? */ 189 int fieldsInHash = 64 / bitsPerField; // max fields used 190 /** A mask with the lower <code>bitsPerField</code> bits set */ 191 long mask = // per-field bit mask 192 0xffffffffffffffffL >>> (64 - bitsPerField); 193 long hash = 0; // current hash value 194 195 // field counts will be different if rep is a template of a superclass 196 long endField = Math.min(fieldsInHash, rep.numFields()); 197 198 // set the appropriate rep of the overall hash for the field's hash 199 for (int i = 0; i < endField; i++) 200 hash |= (hashForField(rep, i) & mask) << (i * bitsPerField); 201 202 result [0] = hash; 203 result [1] = bitsPerField; 204 result [2] = fieldsInHash; 205 result [3] = mask; 206 return result; 207 } 208 209 /** 210 * Return the template description -- mask and hash. 211 * 212 * @see EntryHandleTmplDesc 213 */ 214 static EntryHandleTmplDesc descFor(EntryRep tmpl, int numFields) { 215 EntryHandleTmplDesc tmplDesc; 216 217 // Get the hash and the related useful information 218 long [] hashDesc = hashFor(tmpl, numFields); 219 long hash = hashDesc[0]; 220 long bitsPerField = hashDesc [1]; 221 long fieldsInHash = hashDesc [2]; 222 long mask = hashDesc [3]; 223 224 long tmplMask = 0; 225 226 // Create the mask to mask away wildcard fields 227 for (int i = 0; i < fieldsInHash; i++) { 228 // If this field is one we have a value for, set bits in the mask 229 if (i < tmpl.numFields() && tmpl.value(i) != null) 230 tmplMask |= (mask << (i * bitsPerField)); 231 } 232 233 // Ensure that the non-value fields are masked out 234 hash &= tmplMask; 235 236 tmplDesc = new EntryHandleTmplDesc(hash, tmplMask); 237 238 return tmplDesc; 239 } 240 241 /** 242 * Return the hash value for a given field, which is then merged in 243 * as part of the overall hash for the entry. The last 32 bytes of 244 * the field value are used (or fewer if there are fewer). 245 * 246 * @see #hashFor(EntryRep,int) 247 */ 248 static long hashForField(EntryRep rep, int field) { 249 MarshalledInstance v = rep.value(field); 250 if (v == null) // for templates, it's just zero 251 return 0; 252 else 253 return v.hashCode(); 254 } 255 256 public String toString() { 257 return "0x" + Long.toHexString(hash) + " [" + rep() + "]"; 258 } 259 260 /** 261 * Return <code>true</code> if the operation <code>op</code> under 262 * the given transaction (represented by the transaction's manager) 263 * can be performed on the object represented by this handle. The 264 * thread calling this method should own this object's lock. 265 */ 266 // $$$ Calling this method when we don't own the lock on this 267 // object seems a bit dicey, but that is exactly what we do in 268 // EntryHolder.SimpleRepEnum.nextRep(). Working it through 269 // it seems to work in that particular case, but it seems fragile. 270 boolean canPerform(TransactableMgr mgr, int op) { 271 synchronized (this){ // Audit revealed calling thread didn't always own lock see comment above. 272 if (txnState == null) 273 return true; // all operations are legal on a non-transacted entry 274 275 return txnState.canPerform(mgr, op); 276 } 277 } 278 279 /** 280 * Return <code>true</code> if the given transaction is already 281 * known to the entry this handle represents. The 282 * thread calling this method should own this object's lock. 283 */ 284 boolean knownMgr(TransactableMgr mgr) { 285 synchronized (this){ // Audit revealed caller methods didn't always hold lock. 286 if (txnState == null) 287 return (mgr == null); // The only mgr we know about is the null mgr 288 289 return txnState.knownMgr(mgr); 290 } 291 } 292 293 /** 294 * Return <code>true</code> if we are being managed the given 295 * manager is the only one we know about. The thread calling this 296 * method should own this object's lock. 297 */ 298 boolean onlyMgr(TransactableMgr mgr) { 299 if (txnState == null) // Audit revealed caller always held lock. 300 return false; 301 302 return txnState.onlyMgr(mgr); 303 } 304 305 /** 306 * Return <code>true</code> if the entry this handle represents is 307 * being managed within any transaction. The thread calling this 308 * method should own this object's lock. 309 */ 310 boolean managed() { 311 synchronized (this){ // Audit revealed caller didn't always have lock. 312 return txnState != null; 313 } 314 } 315 316 /** 317 * Add into the collection any transactions that are known to this 318 * handle. The thread calling this method should own this object's 319 * lock. 320 */ 321 void addTxns(java.util.Collection collection) { 322 if (txnState == null) // Confirmed that calling method owns lock. 323 return; // nothing to add 324 325 txnState.addTxns(collection); 326 } 327 328 /** 329 * Add <code>mgr</code> to the list of known managers, setting the 330 * the type of lock on this entry to <code>op</code>. The thread 331 * calling this method should own this object's lock. Assumes 332 * that <code>op</code> is compatible with any lock currently 333 * associated with this entry. <code>holder</code> is the the 334 * <code>EntryHolder</code> holding this handle. 335 */ 336 void add(TransactableMgr mgr, int op, EntryHolder holder) { 337 synchronized (this){ 338 if (txnState == null) { 339 txnState = new TxnState(mgr, op, holder); 340 } else { 341 txnState.add(mgr, op); 342 } 343 } 344 } 345 346 /** 347 * It this entry is read locked promote to take locked and return 348 * true, otherwise return false. Assumes that the object is 349 * locked and the take is being performed under the one 350 * transaction that owns a lock on the entry. 351 */ 352 boolean promoteToTakeIfNeeded() { 353 synchronized (this){ 354 return txnState.promoteToTakeIfNeeded(); 355 } 356 } 357 358 /** 359 * Returns <code>true</code> it this entry has been removed 360 * outside of a transaction, but that removal has not yet been 361 * committed to disk.The thread calling this method should own this 362 * object's lock. 363 */ 364 boolean isProvisionallyRemoved() { 365 assert Thread.holdsLock(this); 366 return removePending; 367 } 368 369 /** 370 * Marks this entry as being removed outside of a transaction but 371 * not yet committed to disk. The thread calling this method should 372 * own this object's lock. 373 */ 374 void provisionallyRemove() { 375 assert Thread.holdsLock(this); 376 assert !removePending; 377 removePending = true; 378 } 379 380 /** 381 * Called after the removal of a provisionally removed entry has 382 * been committed to disk and the handle has been removed from its 383 * holder. The thread calling this method should own this object's 384 * lock. 385 */ 386 void removalComplete() { 387 assert Thread.holdsLock(this); 388 389 if (removePending) { 390 removePending = false; 391 notifyAll(); 392 } 393 } 394 395 /** 396 * If this entry has been marked for removal by a 397 * non-transactional operation, but that operation has not be 398 * yet been committed to disk, block until the operation has been 399 * committed to disk, otherwise return immediately. The 400 * thread calling this method should own this object's lock. 401 */ 402 void waitOnCompleteRemoval() throws InterruptedException { 403 assert Thread.holdsLock(this); 404 while (removePending) { 405 wait(); 406 } 407 } 408 409 /************************************************** 410 * Methods required by the Transactable interface 411 **************************************************/ 412 public synchronized int prepare(TransactableMgr mgr, 413 OutriggerServerImpl space) 414 { 415 if (txnState == null) 416 throw new IllegalStateException("Can't prepare an entry not " + 417 "involved in a transaction"); 418 final int rslt = txnState.prepare(mgr, space, this); 419 if (txnState.empty()) 420 txnState = null; 421 422 return rslt; 423 } 424 425 public synchronized void abort(TransactableMgr mgr, 426 OutriggerServerImpl space) 427 { 428 if (txnState == null) 429 throw new IllegalStateException("Can't abort an entry not " + 430 "involved in a transaction"); 431 final boolean last = txnState.abort(mgr, space, this); 432 if (last) 433 txnState = null; 434 } 435 436 public synchronized void commit(TransactableMgr mgr, 437 OutriggerServerImpl space) 438 { 439 if (txnState == null) 440 throw new IllegalStateException("Can't commit an entry not " + 441 "involved in a transaction"); 442 443 final boolean last = txnState.commit(mgr, space, this); 444 if (last) 445 txnState = null; 446 } 447 448 @Override 449 public synchronized boolean removed() { 450 return removed; 451 } 452 453 public synchronized boolean remove() { 454 if (!removed) removed = super.remove(); 455 return removed; 456 } 457 }