org.jgroups.util.NonBlockingCredit Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.util;
import org.jgroups.BaseMessage;
import org.jgroups.Message;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.function.Consumer;
/**
* Non-blocking credit for a unicast destination.
* Instead of blocking when insufficient credits are available for sending a message, this class queues the
* message and sends it at a later time when enough credits have been received to send it.
* JIRA: https://issues.redhat.com/browse/JGRP-2172
* @author Bela Ban
* @since 4.0.4
*/
public class NonBlockingCredit extends Credit {
protected final SizeBoundedQueue msg_queue;
protected boolean queuing; // messages will be queued when true
protected final Consumer send_function;
protected static final Consumer NO_OP_SEND_FUNCTION=msg -> {};
protected final LongAdder num_queued=new LongAdder();
public NonBlockingCredit(long credits, int max_size, Lock lock) {
this(credits, max_size, lock, NO_OP_SEND_FUNCTION);
}
public NonBlockingCredit(long credits, int max_size, Lock lock, final Consumer send_function) {
super(credits, lock);
this.msg_queue=new SizeBoundedQueue<>(max_size, lock);
this.send_function=send_function;
}
public boolean isQueuing() {return queuing;}
public int getQueuedMessages() {return msg_queue.getElements();}
public int getQueuedMessageSize() {return msg_queue.size();}
public int getEnqueuedMessages() {return num_queued.intValue();}
public void resetStats() {
super.resetStats();
num_queued.reset();
}
/**
* Decrements the sender's credits by the size of the message.
* @param msg The message
* @param credits The number of bytes to decrement the credits. Is {@link BaseMessage#getLength()}.
* @param timeout Ignored
* @return True if the message was sent, false if it was queued
*/
public boolean decrementIfEnoughCredits(final Message msg, int credits, long timeout) {
lock.lock();
try {
if(done)
return false;
if(queuing)
return addToQueue(msg, credits);
if(decrement(credits))
return true; // enough credits, message will be sent
queuing=true; // not enough credits, start queuing
return addToQueue(msg, credits);
}
finally {
lock.unlock();
}
}
public void increment(long credits, long max_credits) {
List drain_list;
lock.lock();
try {
super.increment(credits, max_credits);
if(!queuing || msg_queue.isEmpty())
return;
int drained=msg_queue.drainTo(drain_list=new ArrayList<>(msg_queue.getElements()), (int)this.credits_left);
if(drained > 0)
credits_left=Math.min(max_credits, credits_left - drained);
if(msg_queue.isEmpty())
queuing=false;
}
finally {
lock.unlock();
}
// finally send drained messages:
if(!drain_list.isEmpty())
drain_list.forEach(send_function);
}
public Credit reset() {
lock.lock();
try {
super.reset();
queuing=false;
msg_queue.clear(true);
return this;
}
finally {
lock.unlock();
}
}
public String toString() {
return String.format("%s bytes left (queuing: %b, msg-queue size: %d, bytes: %s, enqueued: %d)",
super.toString(), isQueuing(), getQueuedMessages(), Util.printBytes(getQueuedMessageSize()), num_queued.intValue());
}
protected boolean addToQueue(Message msg, int length) {
try {
msg_queue.add(msg, length);
num_queued.increment();
}
catch(InterruptedException e) {
}
return false;
}
}