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

org.jgroups.util.MaxOneThreadPerSender 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).

There is a newer version: 34.0.0.Final
Show newest version
package org.jgroups.util;

import org.jgroups.Address;
import org.jgroups.Message;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.annotations.Property;
import org.jgroups.protocols.TP;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * {@link org.jgroups.stack.MessageProcessingPolicy} which processes regular messages and message batches by
 * assigning a max of 1 thread per message from the same sender. So if we have senders A, B, C and D, we'll have no more
 * than 4 threads handling regular unicasts and 4 threads handling regular multicasts.

* See JGRP-2143 for details.
* When using virtual threads, we probably don't need this anymore, and can use a virtual thread to send messages / * message batches directly up to UNICAST or NAKACK. * @author Bela Ban * @since 4.0 */ public class MaxOneThreadPerSender extends SubmitToThreadPool { protected final MessageTable mcasts=new MessageTable(); protected final MessageTable ucasts=new MessageTable(); @Property(description="Max number of messages buffered for consumption of the delivery thread in " + "MaxOneThreadPerSender. 0 creates an unbounded buffer") protected int max_buffer_size; @ManagedOperation(description="Dumps unicast and multicast tables") public String dump() { return String.format("\nmcasts:\n%s\nucasts:\n%s", mcasts, ucasts); } public void reset() { mcasts.map.values().forEach(Entry::reset); ucasts.map.values().forEach(Entry::reset); } public void init(TP transport) { super.init(transport); } public void destroy() { mcasts.clear(); ucasts.clear(); } public boolean loopback(Message msg, boolean oob) { if(oob) return super.loopback(msg, oob); MessageTable table=msg.getDest() == null? mcasts : ucasts; return table.process(msg, true); } public boolean process(Message msg, boolean oob) { if(oob) return super.process(msg, oob); MessageTable table=msg.getDest() == null? mcasts : ucasts; return table.process(msg, false); } public boolean process(MessageBatch batch, boolean oob) { if(oob) return super.process(batch, oob); MessageTable table=batch.dest() == null? mcasts : ucasts; return table.process(batch); } public void viewChange(List

members) { mcasts.viewChange(members); ucasts.viewChange(members); } protected class MessageTable { protected final ConcurrentMap map=new ConcurrentHashMap<>(); public MessageTable() { } protected Entry get(final Address sender, boolean multicast) { return map.computeIfAbsent(sender, s -> new Entry(sender, multicast, tp.getClusterNameAscii())); } protected void clear() {map.clear();} protected boolean process(Message msg, boolean loopback) { Address dest=msg.getDest(), sender=msg.getSrc(); return sender != null && get(sender, dest == null).process(msg, loopback); } protected boolean process(MessageBatch batch) { Address dest=batch.dest(), sender=batch.sender(); return get(sender, dest == null).process(batch); } protected void viewChange(List
mbrs) { // remove all senders that are not in the new view map.keySet().retainAll(mbrs); } public String toString() { return map.entrySet().stream().map(e -> String.format("%s: %s", e.getKey(), e.getValue())).collect(Collectors.joining("\n")); } } protected class Entry { protected final Lock lock=new ReentrantLock(); protected boolean running; // if true, a thread is delivering a message batch to the application protected final boolean mcast; protected final MessageBatch batch; // used to queue messages protected final Address sender; protected final AsciiString cluster_name; protected long submitted_msgs; protected long submitted_batches; protected long queued_msgs; protected long queued_batches; protected Entry(Address sender, boolean mcast, AsciiString cluster_name) { this.mcast=mcast; this.sender=sender; this.cluster_name=cluster_name; int cap=max_buffer_size > 0? max_buffer_size : 16; // initial capacity batch=new MessageBatch(cap).dest(tp.getAddress()).sender(sender).clusterName(cluster_name).multicast(mcast); } public Entry reset() { submitted_msgs=submitted_batches=queued_msgs=queued_batches=0; return this; } protected boolean process(Message msg, boolean loopback) { if(!allowedToSubmitToThreadPool(msg)) return false; // running is true, we didn't queue msg and need to submit a task to the thread pool return submit(msg, loopback); } protected boolean process(MessageBatch batch) { if(!allowedToSubmitToThreadPool(batch)) return false; // running is true, we didn't queue msg and need to submit a task to the thread pool return submit(batch); } protected boolean submit(Message msg, boolean loopback) { // running is true, we didn't queue msg and need to submit a task to the thread pool try { submitted_msgs++; MessageBatch mb=new MessageBatch(16).sender(sender).dest(mcast? null : tp.getAddress()) .clusterName(cluster_name).multicast(mcast).add(msg); BatchHandlerLoop handler=new BatchHandlerLoop(mb, this, loopback); if(!tp.getThreadPool().execute(handler)) { setRunning(false); return false; } return true; } catch(Throwable t) { setRunning(false); return false; } } protected boolean submit(MessageBatch batch) { try { submitted_batches++; BatchHandlerLoop handler=new BatchHandlerLoop(batch, this, false); if(!tp.getThreadPool().execute(handler)) { setRunning(false); return false; } return true; } catch(Throwable t) { setRunning(false); return false; } } /** * Either allows access to submit a task to the thread pool for delivery to the application, or queues the * message * @param msg the message * @return true if the message can be submitted to the thread pool, or false (msg was queued) */ protected boolean allowedToSubmitToThreadPool(Message msg) { lock.lock(); try { if(!running) return running=true; // the caller can submit a new BatchHandlerLoop task to the thread pool this.batch.add(msg, max_buffer_size == 0); queued_msgs++; return false; } finally { lock.unlock(); } } protected boolean allowedToSubmitToThreadPool(MessageBatch msg_batch) { lock.lock(); try { if(!running) return running=true; // the caller can submit a new BatchHandlerLoop task to the thread pool this.batch.add(msg_batch, max_buffer_size == 0); queued_batches++; return false; } finally { lock.unlock(); } } /** Called by {@link BatchHandlerLoop}. Atomically transfer messages from the entry's batch to this batch and * returns true if messages were transferred. If not, sets running to false and returns false. In the latter * case, the handler must terminate (or else, we could have multiple handler running). * @param msg_batch the batch to which messages from this.batch should be transferred to. */ protected boolean workAvailable(final MessageBatch msg_batch) { lock.lock(); try { int num_msgs=msg_batch.transferFrom(this.batch, true); return num_msgs > 0 || (running=false); } catch(Throwable t) { return running=false; } finally { lock.unlock(); } } protected void setRunning(boolean flag) { lock.lock(); try { running=flag; } finally { lock.unlock(); } } // unsynchronized on batch but who cares public String toString() { return String.format("batch size=%d queued msgs=%d queued batches=%d submitted msgs=%d submitted batches=%d", batch.size(), queued_msgs, queued_batches, submitted_msgs, submitted_batches); } } public class BatchHandlerLoop extends BatchHandler { protected final Entry entry; protected final boolean loopback; public BatchHandlerLoop(MessageBatch batch, Entry entry, boolean loopback) { super(batch); this.entry=entry; this.loopback=loopback; } public void run() { do { try { super.run(); } catch(Throwable t) { log.error("failed processing batch", t); } } while(entry.workAvailable(this.batch)); // transfers msgs from entry.batch --> this.batch // worker termination: workAvailable() already set running=false } @Override protected void passBatchUp() { tp.passBatchUp(batch, !loopback, !loopback); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy