/** * The method will borrow a BagEntry from the bag, blocking for the specified timeout if none are * available. * * @param timeout how long to wait before giving up, in units of unit * @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter * @return a borrowed instance from the bag or null if a timeout occurs * @throws InterruptedException if interrupted while waiting */ public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException { // Try the thread-local list first List<Object> list = threadList.get(); if (weakThreadLocals && list == null) { list = new ArrayList<>(16); threadList.set(list); } for (int i = list.size() - 1; i >= 0; i--) { final Object entry = list.remove(i); @SuppressWarnings("unchecked") final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry; if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { return bagEntry; } } // Otherwise, scan the shared list ... for maximum of timeout timeout = timeUnit.toNanos(timeout); Future<Boolean> addItemFuture = null; final long startScan = System.nanoTime(); final long originTimeout = timeout; long startSeq; waiters.incrementAndGet(); try { do { // scan the shared list do { startSeq = synchronizer.currentSequence(); for (T bagEntry : sharedList) { if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { // if we might have stolen another thread's new connection, restart the add... if (waiters.get() > 1 && addItemFuture == null) { listener.addBagItem(); } return bagEntry; } } } while (startSeq < synchronizer.currentSequence()); if (addItemFuture == null || addItemFuture.isDone()) { addItemFuture = listener.addBagItem(); } timeout = originTimeout - (System.nanoTime() - startScan); } while (timeout > 10_000L && synchronizer.waitUntilSequenceExceeded(startSeq, timeout)); } finally { waiters.decrementAndGet(); } return null; }
/** * This method is used to make an item reserved via <code>reserve(T)</code> available again for * borrowing. * * @param bagEntry the item to unreserve */ public void unreserve(final T bagEntry) { if (bagEntry.compareAndSet(STATE_RESERVED, STATE_NOT_IN_USE)) { synchronizer.signal(); } else { LOGGER.warn("Attempt to relinquish an object to the bag that was not reserved: {}", bagEntry); } }
/** * Remove a value from the bag. This method should only be called with objects obtained by <code> * borrow(long, TimeUnit)</code> or <code>reserve(T)</code> * * @param bagEntry the value to remove * @return true if the entry was removed, false otherwise * @throws IllegalStateException if an attempt is made to remove an object from the bag that was * not borrowed or reserved first */ public boolean remove(final T bagEntry) { if (!bagEntry.compareAndSet(STATE_IN_USE, STATE_REMOVED) && !bagEntry.compareAndSet(STATE_RESERVED, STATE_REMOVED) && !closed) { LOGGER.warn( "Attempt to remove an object from the bag that was not borrowed or reserved: {}", bagEntry); return false; } final boolean removed = sharedList.remove(bagEntry); if (!removed && !closed) { LOGGER.warn("Attempt to remove an object from the bag that does not exist: {}", bagEntry); } // synchronizer.signal(); return removed; }
/** * The method is used to make an item in the bag "unavailable" for borrowing. It is primarily used * when wanting to operate on items returned by the <code>values(int)</code> method. Items that * are reserved can be removed from the bag via <code>remove(T)</code> without the need to * unreserve them. Items that are not removed from the bag can be make available for borrowing * again by calling the <code>unreserve(T)</code> method. * * @param bagEntry the item to reserve * @return true if the item was able to be reserved, false otherwise */ public boolean reserve(final T bagEntry) { return bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_RESERVED); }