Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.jgroups.protocols;
import org.jgroups.Address;
import org.jgroups.Global;
import org.jgroups.Message;
import org.jgroups.annotations.Property;
import org.jgroups.util.RingBuffer;
import org.jgroups.util.Runner;
import org.jgroups.util.Util;
import java.util.Objects;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
/**
* Bundler which uses {@link RingBuffer} to store messages. The difference to {@link TransferQueueBundler} is that
* RingBuffer uses a wait strategy (to for example spinning) before blocking. Also, the hashmap of the superclass is not
* used, but the array of the RingBuffer is used directly to bundle and send messages, minimizing memory allocation.
*/
public class RingBufferBundler extends BaseBundler {
protected RingBuffer rb;
protected Runner bundler_thread;
@Property(description="Number of spins before a real lock is acquired")
protected int num_spins=40; // number of times we call Thread.yield before acquiring the lock (0 disables)
protected static final String THREAD_NAME="RingBufferBundler";
protected BiConsumer wait_strategy=SPIN_PARK;
protected final Runnable run_function=this::readMessages;
protected static final BiConsumer SPIN=(it,spins) -> {;};
protected static final BiConsumer YIELD=(it,spins) -> Thread.yield();
protected static final BiConsumer PARK=(it,spins) -> LockSupport.parkNanos(1);
protected static final BiConsumer SPIN_PARK=(it, spins) -> {
if(it < spins/10)
return; // spin for the first 10% of all iterations, then switch to park()
LockSupport.parkNanos(1);
};
protected static final BiConsumer SPIN_YIELD=(it, spins) -> {
if(it < spins/10)
return; // spin for the first 10% of the total number of iterations
Thread.yield(); //, then switch to yield()
};
public RingBufferBundler() {
}
protected RingBufferBundler(RingBuffer rb) {
this.rb=rb;
this.capacity=rb.capacity();
}
public RingBufferBundler(int capacity) {
this(new RingBuffer<>(Message.class, assertPositive(capacity, "bundler capacity cannot be " + capacity)));
}
public RingBuffer buf() {return rb;}
public int size() {return rb.size();}
public int getQueueSize() {return rb.size();}
public int numSpins() {return num_spins;}
public RingBufferBundler numSpins(int n) {num_spins=n; return this;}
@Property(description="The wait strategy: spin, yield, park, spin-park, spin-yield",writable=false)
public String waitStrategy() {return print(wait_strategy);}
@Property
public RingBufferBundler waitStrategy(String st) {wait_strategy=createWaitStrategy(st, YIELD); return this;}
public void init(TP transport) {
super.init(transport);
if(rb == null) {
rb=new RingBuffer<>(Message.class, assertPositive(capacity, "bundler capacity cannot be " + capacity));
this.capacity=rb.capacity();
}
bundler_thread=new Runner(transport.getThreadFactory(), THREAD_NAME, run_function, () -> rb.clear());
}
public void start() {
bundler_thread.start();
}
public void stop() {
bundler_thread.stop();
}
public void renameThread() {
transport.getThreadFactory().renameThread(THREAD_NAME, bundler_thread.getThread());
}
public void send(Message msg) throws Exception {
rb.put(msg);
}
/** Read and send messages in range [read-index .. read-index+available_msgs-1] */
public void sendBundledMessages(final Message[] buf, final int read_index, final int available_msgs) {
byte[] cluster_name=transport.cluster_name.chars();
int start=read_index;
final int end=index(start + available_msgs-1); // index of the last message to be read
for(;;) {
Message msg=buf[start];
if(msg == null) {
if(start == end)
break;
start=advance(start);
continue;
}
Address dest=msg.getDest();
try {
output.position(0);
Util.writeMessageListHeader(dest, msg.getSrc(), cluster_name, 1, output, dest == null);
// remember the position at which the number of messages (an int) was written, so we can later set the
// correct value (when we know the correct number of messages)
int size_pos=output.position() - Global.INT_SIZE;
int num_msgs=marshalMessagesToSameDestination(dest, buf, start, end, max_size);
if(num_msgs > 1) {
int current_pos=output.position();
output.position(size_pos);
output.writeInt(num_msgs);
output.position(current_pos);
}
transport.doSend(output.buffer(), 0, output.position(), dest);
transport.getMessageStats().incrNumBatchesSent(num_msgs);
}
catch(Exception ex) {
log.trace("failed to send message(s) to %s: %s", dest == null? "group" : dest, ex.getMessage());
}
if(start == end)
break;
start=advance(start);
}
}
// Iterate through the following messages and find messages to the same destination (dest) and write them to output
protected int marshalMessagesToSameDestination(Address dest, Message[] buf,
int start_index, final int end_index, int max_bundle_size) throws Exception {
int num_msgs=0, bytes=0;
for(;;) {
Message msg=buf[start_index];
if(msg != null && Objects.equals(dest, msg.getDest())) {
int size=msg.size() + Global.SHORT_SIZE;
if(bytes + size > max_bundle_size)
break;
bytes+=size;
num_msgs++;
buf[start_index]=null;
output.writeShort(msg.getType());
msg.writeToNoAddrs(msg.getSrc(), output);
}
if(start_index == end_index)
break;
start_index=advance(start_index);
}
return num_msgs;
}
protected void readMessages() {
try {
int available_msgs=rb.waitForMessages(num_spins, wait_strategy);
int read_index=rb.readIndexLockless();
Message[] buf=rb.buf();
sendBundledMessages(buf, read_index, available_msgs);
rb.publishReadIndex(available_msgs);
}
catch(Throwable t) {
;
}
}
protected final int advance(int index) {return index+1 == capacity? 0 : index+1;}
protected final int index(int idx) {return idx & (capacity-1);} // fast equivalent to %
protected static String print(BiConsumer wait_strategy) {
if(wait_strategy == null) return null;
if(wait_strategy == SPIN) return "spin";
else if(wait_strategy == YIELD) return "yield";
else if(wait_strategy == PARK) return "park";
else if(wait_strategy == SPIN_PARK) return "spin-park";
else if(wait_strategy == SPIN_YIELD) return "spin-yield";
else return wait_strategy.getClass().getSimpleName();
}
protected BiConsumer createWaitStrategy(String st, BiConsumer default_wait_strategy) {
if(st == null) return default_wait_strategy;
switch(st) {
case "spin": return wait_strategy=SPIN;
case "yield": return wait_strategy=YIELD;
case "park": return wait_strategy=PARK;
case "spin_park":
case "spin-park": return wait_strategy=SPIN_PARK;
case "spin_yield":
case "spin-yield": return wait_strategy=SPIN_YIELD;
default:
try {
Class> clazz=(Class>)Util.loadClass(st, this.getClass());
return clazz.getDeclaredConstructor().newInstance();
}
catch(Throwable t) {
log.error("failed creating wait_strategy " + st, t);
return default_wait_strategy;
}
}
}
protected static int assertPositive(int value, String message) {
if(value <= 0) throw new IllegalArgumentException(message);
return value;
}
}