All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rabbitmq.client.impl.WorkPool Maven / Gradle / Ivy

Go to download

The RabbitMQ Java client library allows Java applications to interface with RabbitMQ.

There is a newer version: 5.22.0
Show newest version
package com.rabbitmq.client.impl;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * This is a generic implementation of the Channels specification
 * in Channeling Work, Nov 2010 (channels.pdf).
 * 

* Objects of type K must be registered, with registerKey(K), * and then they become clients and a queue of * items (type W) is stored for each client. *

* Each client has a state which is exactly one of dormant, * in progress or ready. Immediately after registration a client is dormant. *

* Items may be (singly) added to (the end of) a client's queue with addWorkItem(K,W). * If the client is dormant it becomes ready thereby. All other states remain unchanged. *

* The next ready client, together with a collection of its items, * may be retrieved with nextWorkBlock(collection,max) * (making that client in progress). *

* An in progress client can finish (processing a batch of items) with finishWorkBlock(K). * It then becomes either dormant or ready, depending if its queue of work items is empty or no. *

* If a client has items queued, it is either in progress or ready but cannot be both. * When work is finished it may be marked ready if there is further work, * or dormant if there is not. * There is never any work for a dormant client. *

* A client may be unregistered, with unregisterKey(K), which removes the client from * all parts of the state, and any queue of items stored with it. * All clients may be unregistered with unregisterAllKeys(). *

* Concurrent Semantics
* This implementation is thread-safe. *

* Implementation Notes
* The state is, roughly, as follows: *

 pool :: map(K, seq W)
 * inProgress :: set K
 * ready :: iseq K
*

* where a seq is a sequence (queue or list) and an iseq * (i for injective) is a sequence with no duplicates. *

* State transitions

 *      finish(k)            -------------
 *             -----------> | (dormant)   |
 *            |              -------------
 *  -------------  next()        | add(item)
 * | in progress | <---------    |
 *  -------------            |   V
 *            |              -------------
 *             -----------> | ready       |
 *      finish(k)            -------------
 * 
* dormant is not represented in the implementation state, and adding items * when the client is in progress or ready does not change its state. * @param Key -- type of client * @param Work -- type of work item */ public class WorkPool { private static final int MAX_QUEUE_LENGTH = 1000; /** An injective queue of ready clients. */ private final SetQueue ready = new SetQueue(); /** The set of clients which have work in progress. */ private final Set inProgress = new HashSet(); /** The pool of registered clients, with their work queues. */ private final Map> pool = new HashMap>(); /** Those keys which want limits to be removed. We do not limit queue size if this is non-empty. */ private final Set unlimited = new HashSet(); /** * Add client key to pool of item queues, with an empty queue. * A client is initially dormant. *

* No-op if key already present. * @param key client to add to pool */ public void registerKey(K key) { synchronized (this) { if (!this.pool.containsKey(key)) { int initialCapacity = unlimited.isEmpty() ? MAX_QUEUE_LENGTH : Integer.MAX_VALUE; this.pool.put(key, new VariableLinkedBlockingQueue(initialCapacity)); } } } public synchronized void limit(K key) { unlimited.remove(key); if (unlimited.isEmpty()) { setCapacities(MAX_QUEUE_LENGTH); } } public synchronized void unlimit(K key) { unlimited.add(key); if (!unlimited.isEmpty()) { setCapacities(Integer.MAX_VALUE); } } private void setCapacities(int capacity) { Iterator> it = pool.values().iterator(); while (it.hasNext()) { it.next().setCapacity(capacity); } } /** * Remove client from pool and from any other state. Has no effect if client already absent. * @param key of client to unregister */ public void unregisterKey(K key) { synchronized (this) { this.pool.remove(key); this.ready.remove(key); this.inProgress.remove(key); } } /** * Remove all clients from pool and from any other state. */ public void unregisterAllKeys() { synchronized (this) { this.pool.clear(); this.ready.clear(); this.inProgress.clear(); } } /** * Return the next ready client, * and transfer a collection of that client's items to process. * Mark client in progress. *

* If there is no ready client, return null. * @param to collection object in which to transfer items * @param size max number of items to transfer * @return key of client to whom items belong, or null if there is none. */ public K nextWorkBlock(Collection to, int size) { synchronized (this) { K nextKey = readyToInProgress(); if (nextKey != null) { VariableLinkedBlockingQueue queue = this.pool.get(nextKey); drainTo(queue, to, size); } return nextKey; } } /** * Private implementation of drainTo (not implemented for LinkedList<W>s). * @param deList to take (poll) elements from * @param c to add elements to * @param maxElements to take from deList * @return number of elements actually taken */ private int drainTo(VariableLinkedBlockingQueue deList, Collection c, int maxElements) { int n = 0; while (n < maxElements) { W first = deList.poll(); if (first == null) break; c.add(first); ++n; } return n; } /** * Add (enqueue) an item for a specific client. * No change and returns false if client not registered. * If dormant, the client will be marked ready. * @param key the client to add to the work item to * @param item the work item to add to the client queue * @return true if and only if the client is marked ready * — as a result of this work item */ public boolean addWorkItem(K key, W item) { VariableLinkedBlockingQueue queue; synchronized (this) { queue = this.pool.get(key); } // The put operation may block. We need to make sure we are not holding the lock while that happens. if (queue != null) { try { queue.put(item); } catch (InterruptedException e) { // ok } synchronized (this) { if (isDormant(key)) { dormantToReady(key); return true; } } } return false; } /** * Set client no longer in progress. * Ignore unknown clients (and return false). * @param key client that has finished work * @return true if and only if client becomes ready * @throws IllegalStateException if registered client not in progress */ public boolean finishWorkBlock(K key) { synchronized (this) { if (!this.isRegistered(key)) return false; if (!this.inProgress.contains(key)) { throw new IllegalStateException("Client " + key + " not in progress"); } if (moreWorkItems(key)) { inProgressToReady(key); return true; } else { inProgressToDormant(key); return false; } } } private boolean moreWorkItems(K key) { VariableLinkedBlockingQueue leList = this.pool.get(key); return leList != null && !leList.isEmpty(); } /* State identification functions */ private boolean isInProgress(K key){ return this.inProgress.contains(key); } private boolean isReady(K key){ return this.ready.contains(key); } private boolean isRegistered(K key) { return this.pool.containsKey(key); } private boolean isDormant(K key){ return !isInProgress(key) && !isReady(key) && isRegistered(key); } /* State transition methods - all assume key registered */ private void inProgressToReady(K key){ this.inProgress.remove(key); this.ready.addIfNotPresent(key); } private void inProgressToDormant(K key){ this.inProgress.remove(key); } private void dormantToReady(K key){ this.ready.addIfNotPresent(key); } /* Basic work selector and state transition step */ private K readyToInProgress() { K key = this.ready.poll(); if (key != null) { this.inProgress.add(key); } return key; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy