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.util.Runner;
import org.jgroups.util.Util;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
/**
* Bundler which doesn't use locks but relies on CAS. There is 1 reader thread which gets unparked by (exactly one) writer
* when the max size has been exceeded, or no other threads are sending messages.
* @author Bela Ban
* @since 4.0
*/
public class RingBufferBundlerLockless extends BaseBundler {
protected Message[] buf;
protected int read_index;
protected volatile int write_index=0;
protected final AtomicInteger tmp_write_index=new AtomicInteger(0);
protected final AtomicInteger write_permits; // number of permits to write tmp_write_index
protected final AtomicInteger size=new AtomicInteger(0); // number of messages to be read: read_index + count == write_index
protected final AtomicInteger num_threads=new AtomicInteger(0); // number of threads currently in send()
protected final AtomicLong accumulated_bytes=new AtomicLong(0); // total number of bytes of unread msgs
protected final AtomicBoolean unparking=new AtomicBoolean(false);
protected Runner bundler_thread;
protected static final String THREAD_NAME="RingBufferBundlerLockless";
protected final Runnable run_function=this::readMessages;
public RingBufferBundlerLockless() {
this(1024);
}
public RingBufferBundlerLockless(int capacity) {
buf=new Message[Util.getNextHigherPowerOfTwo(capacity)]; // for efficient % (mod) op
this.write_permits=new AtomicInteger(buf.length);
}
public int readIndex() {return read_index;}
public int writeIndex() {return write_index;}
public int size() {return size.get();}
public int getQueueSize() {return size.get();}
public void init(TP transport) {
super.init(transport);
bundler_thread=new Runner(transport.getThreadFactory(), THREAD_NAME, run_function, this::reset);
}
public void reset() {
read_index=write_index=0;
tmp_write_index.set(0);
size.set(0);
}
public void start() {
bundler_thread.start();
}
public void stop() {
bundler_thread.stop();
}
public void send(Message msg) throws Exception {
if(msg == null)
throw new IllegalArgumentException("message must not be null");
num_threads.incrementAndGet();
int tmp_index=getWriteIndex(); // decrements write_permits
// System.out.printf("[%d] tmp_index=%d\n", Thread.currentThread().getId(), tmp_index);
if(tmp_index == -1) {
log.warn("buf is full (num_permits: %d, bundler: %s)\n", write_permits.get(), toString());
num_threads.decrementAndGet();
return;
}
buf[tmp_index]=msg;
long acc_bytes=accumulated_bytes.addAndGet(msg.size());
int current_threads=num_threads.decrementAndGet();
boolean no_other_threads=current_threads == 0;
boolean unpark=(acc_bytes >= transport.getMaxBundleSize() && accumulated_bytes.compareAndSet(acc_bytes, 0))
|| no_other_threads;
// only 2 threads at a time should do this (1st cond and 2nd cond), so we have to reduce this to
// 1 thread as advanceWriteIndex() is not thread safe
if(unpark && unparking.compareAndSet(false, true)) {
int num_advanced=advanceWriteIndex();
size.addAndGet(num_advanced);
if(num_advanced > 0) {
Thread thread=bundler_thread.getThread();
if(thread != null)
LockSupport.unpark(thread);
}
unparking.set(false);
}
}
protected int getWriteIndex() {
int permit=getPermitToWrite();
if(permit < 0)
return -1;
// here we're guaranteed to have space available for writing, we now need to find the right one
int next=tmp_write_index.getAndIncrement();
int next_index=index(next);
tmp_write_index.compareAndSet(next, next_index);
return next_index;
}
protected int getPermitToWrite() {
int remaining=write_permits.decrementAndGet();
if(remaining < 0)
write_permits.incrementAndGet();
return remaining;
}
// Advance write_index up to tmp_write_index as long as no null msg is found
protected int advanceWriteIndex() {
int num=0, start=write_index;
for(;;) {
if(buf[start] == null)
break;
num++;
start=index(start+1);
if(start == tmp_write_index.get())
break;
}
write_index=start;
return num;
}
protected void readMessages() {
_readMessages();
LockSupport.park();
}
/** Read and send messages in range [read-index .. read-index+available_msgs-1] */
protected int sendBundledMessages(final Message[] buf, final int read_index, int available_msgs) {
int max_bundle_size=transport.getMaxBundleSize();
byte[] cluster_name=transport.cluster_name.chars();
int start=read_index;
int sent_msgs=0;
while(available_msgs > 0) {
Message msg=buf[start];
if(msg == null) {
start=increment(start);
available_msgs--;
continue;
}
Address dest=msg.dest();
try {
output.position(0);
Util.writeMessageListHeader(dest, msg.src(), 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, available_msgs, max_bundle_size);
sent_msgs+=num_msgs;
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);
if(transport.statsEnabled())
transport.incrBatchesSent(num_msgs);
}
catch(Exception ex) {
log.error("failed to send message(s)", ex);
}
available_msgs--;
start=increment(start);
}
return sent_msgs;
}
public String toString() {
return String.format("read-index=%d write-index=%d size=%d cap=%d", read_index, write_index, size.get(), buf.length);
}
public int _readMessages() {
int available_msgs=size.get();
if(available_msgs > 0) {
int sent_msgs=sendBundledMessages(buf, read_index, available_msgs);
read_index=index(read_index + sent_msgs);
size.addAndGet(-sent_msgs);
write_permits.addAndGet(sent_msgs);
return sent_msgs;
}
return 0;
}
// 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, int available_msgs, int max_bundle_size) throws Exception {
int num_msgs=0, bytes=0;
while(available_msgs > 0) {
Message msg=buf[start_index];
if(msg != null && Objects.equals(dest, msg.dest())) {
long msg_size=msg.size();
if(bytes + msg_size > max_bundle_size)
break;
bytes+=msg_size;
num_msgs++;
buf[start_index]=null;
msg.writeToNoAddrs(msg.src(), output, transport.getId());
}
available_msgs--;
start_index=increment(start_index);
}
return num_msgs;
}
protected final int increment(int index) {return index+1 == buf.length? 0 : index+1;}
protected final int index(int idx) {return idx & (buf.length-1);} // fast equivalent to %
protected static int assertPositive(int value, String message) {
if(value <= 0) throw new IllegalArgumentException(message);
return value;
}
}