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

org.jgroups.protocols.RingBufferBundler 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).

The newest version!
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 Thread              getThread()               {return bundler_thread.getThread();}
    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 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);
                if(transport.statsEnabled())
                    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, transport.getId());
            }
            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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy