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

com.gemstone.org.jgroups.stack.AckSenderWindow Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/** Notice of modification as required by the LGPL
 *  This file was modified by Gemstone Systems Inc. on
 *  $Date$
 **/
// $Id: AckSenderWindow.java,v 1.17 2005/08/26 08:56:22 belaban Exp $

package com.gemstone.org.jgroups.stack;


import com.gemstone.org.jgroups.protocols.TP;
import com.gemstone.org.jgroups.util.GemFireTracer;
import com.gemstone.org.jgroups.Address;
import com.gemstone.org.jgroups.Message;
import com.gemstone.org.jgroups.util.ExternalStrings;
import com.gemstone.org.jgroups.util.TimeScheduler;
import com.gemstone.org.jgroups.util.Util;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;


/**
 * ACK-based sliding window for a sender. Messages are added to the window keyed by seqno
 * When an ACK is received, the corresponding message is removed. The Retransmitter
 * continously iterates over the entries in the hashmap, retransmitting messages based on their
 * creation time and an (increasing) timeout. When there are no more messages in the retransmission
 * table left, the thread terminates. It will be re-activated when a new entry is added to the
 * retransmission table.
 * @author Bela Ban
 */
public class AckSenderWindow implements Retransmitter.RetransmitCommand {
    RetransmitCommand   retransmit_command = null;   // called to request XMIT of msg
    final Map     msgs=new ConcurrentHashMap();        // keys: seqnos (Long), values: Messages
    long[]              interval = new long[]{400,800,1200,1600};
    final Retransmitter retransmitter;
    static    final GemFireTracer log=GemFireTracer.getLog(AckSenderWindow.class);
    Address dest = null; // GemStoneAddition - added for debugging so we know dest for xmit tasks
    
    public interface RetransmitCommand {
        void retransmit(long seqno, Message msg);
        // GemStoneAddition - retransmission burst containment (bug #40467)
        long getMaxRetransmissionBurst();
    }


    /**
     * Creates a new instance. Thre retransmission thread has to be started separately with
     * start().
     * @param com If not null, its method retransmit() will be called when a message
     *            needs to be retransmitted (called by the Retransmitter).
     */
    public AckSenderWindow(RetransmitCommand com) {
        retransmit_command = com;
        retransmitter = new Retransmitter(null, this);
        retransmitter.setRetransmitTimeouts(interval);
    }


    public AckSenderWindow(RetransmitCommand com, long[] interval) {
        retransmit_command = com;
        this.interval = interval;
        retransmitter = new Retransmitter(null, this);
        retransmitter.setRetransmitTimeouts(interval);
    }



    public AckSenderWindow(RetransmitCommand com, long[] interval, TimeScheduler sched) {
        retransmit_command = com;
        this.interval = interval;
        retransmitter = new Retransmitter(null, this, sched);
        retransmitter.setRetransmitTimeouts(interval);
    }



    public void reset() {
      synchronized (msgs) { // GemStoneAddition
        msgs.clear();
      }

        // moved out of sync scope: Retransmitter.reset()/add()/remove() are sync'ed anyway
        // Bela Jan 15 2003
        retransmitter.reset();
    }


    /**
     * Adds a new message to the retransmission table. If the message won't have received an ack within
     * a certain time frame, the retransmission thread will retransmit the message to the receiver. If
     * a sliding window protocol is used, we only add up to window_size messages. If the table is
     * full, we add all new messages to a queue. Those will only be added once the table drains below a certain
     * threshold (min_threshold)
     */
    public void add(long seqno, Message msg) {
        Long tmp=Long.valueOf(seqno);
        synchronized(msgs) {  // the contains() and put() should be atomic
          if (this.dest == null) {
            this.dest = msg.getDest();
          }
          if(!msgs.containsKey(tmp))
            msgs.put(tmp, msg);
          retransmitter.add(seqno,seqno);
        }
    }


    /**
     * Removes the message from msgs, removing them also from retransmission. If
     * sliding window protocol is used, and was queueing, check whether we can resume adding elements.
     * Add all elements. If this goes above window_size, stop adding and back to queueing. Else
     * set queueing to false.
     * @return the size of the message if the message was still in the window and required an ack, or -1 if not found
     */
    public long ack(long seqno) {
      final Message msg;
        synchronized(msgs) {
          msg = (Message)msgs.remove(Long.valueOf(seqno));
          if (msg == null) {
            return -1;
          }
          retransmitter.remove(seqno);
        }
        return msg.size();
    }

    public int size() {
      synchronized (msgs) { // GemStoneAddition
        return msgs.size();
      }
    }

    @Override // GemStoneAddition
    public String toString() {
        StringBuffer sb=new StringBuffer();
        //sb.append(msgs.size()).append(" msgs (").append(retransmitter.size()).append(" to retransmit): ");
        sb.append("retransmitter: ");
//        retransmitter.toString(sb);
        TreeSet keys=new TreeSet(msgs.keySet());
        if(keys.size() > 0)
            sb.append('{').append(keys.first()).append(" - ").append(keys.last()).append('}');
        else
            sb.append("{}");
        return sb.toString();
    }


    public String printDetails() {
        StringBuffer sb=new StringBuffer();
        sb.append(msgs.size()).append(" msgs (").append(retransmitter.size()).append(" to retransmit): ").
                append(new TreeSet(msgs.keySet()));
        return sb.toString();
    }

    /* -------------------------------- Retransmitter.RetransmitCommand interface ------------------- */
    public Address getDest() {
      return this.dest;
    }
    
    public void retransmit(long first_seqno, long last_seqno, Address sender) {
        Message msg;
        Address dest = null;
        long burstLimit = retransmit_command.getMaxRetransmissionBurst(); // GemStoneAddition - max burst size
        long burstSize = 0;

        if (retransmit_command != null) {
            for (long i = first_seqno; i <= last_seqno; i++) {
              // GemStoneAddition: fetch msg under synchronization
              synchronized (msgs) {
                msg = (Message) msgs.get(Long.valueOf(i));                
              }
                if(msg != null) { // find the message to retransmit
                    retransmit_command.retransmit(i, msg);
                    dest = msg.getDest();
                    if (burstLimit > 0) {
                      burstSize += msg.size();
                      if (burstSize >= burstLimit) {
                        break;
                      }
                    }
                }
            }
            if (log.isTraceEnabled() || TP.VERBOSE) {
              StringBuffer b = new StringBuffer("retransmitted message");
              if (last_seqno == first_seqno) {
                b.append(' ').append(first_seqno);
              }
              else {
                b.append("s ").append(first_seqno).
                          append(" - ").append(last_seqno);
              }
              if (dest != null) {
                b.append(" to ").append(dest);
              }
              log.getLogWriter().info(ExternalStrings.DEBUG, b);
            }
        }
    }
    /* ----------------------------- End of Retransmitter.RetransmitCommand interface ---------------- */





    /* ---------------------------------- Private methods --------------------------------------- */

    /* ------------------------------ End of Private methods ------------------------------------ */




    /** Struct used to store message alongside with its seqno in the message queue */
    static/*GemStoneAddition*/ class Entry {
        final long seqno;
        final Message msg;

        Entry(long seqno, Message msg) {
            this.seqno = seqno;
            this.msg = msg;
        }
    }


    static class Dummy implements RetransmitCommand {
        static/*GemStoneAddition*/ final long last_xmit_req = 0;
//         long curr_time; GemStoneAddition (omitted)


        public void retransmit(long seqno, Message msg) {

                if(log.isDebugEnabled()) log.debug("seqno=" + seqno);

//            curr_time = System.currentTimeMillis(); GemStoneAddition
        }
        public long getMaxRetransmissionBurst() {
          return 0;
        }
    }


//    public static void main(String[] args) {
//        long[] xmit_timeouts = {1000, 2000, 3000, 4000};
//        AckSenderWindow win = new AckSenderWindow(new Dummy(), xmit_timeouts);
//
//
//
//        final int NUM = 1000;
//
//        for (int i = 1; i < NUM; i++)
//            win.add(i, new Message());
//
//
//        try { // GemStoneAddition
//        System.out.println(win);
//        Util.sleep(5000);
//
//        for (int i = 1; i < NUM; i++) {
//            if (i % 2 == 0) // ack the even seqnos
//                win.ack(i);
//        }
//
//        System.out.println(win);
//        Util.sleep(4000);
//        }
//        catch (InterruptedException e) {
//          return; // this is a main; just exit.
//        }
//
//        for (int i = 1; i < NUM; i++) {
//            if (i % 2 != 0) // ack the odd seqnos
//                win.ack(i);
//        }
//        System.out.println(win);
//
//        win.add(3, new Message());
//        win.add(5, new Message());
//        win.add(4, new Message());
//        win.add(8, new Message());
//        win.add(9, new Message());
//        win.add(6, new Message());
//        win.add(7, new Message());
//        win.add(3, new Message());
//        System.out.println(win);
//
//
//        try {
//            Thread.sleep(5000);
//            win.ack(5);
//            System.out.println("ack(5)");
//            win.ack(4);
//            System.out.println("ack(4)");
//            win.ack(6);
//            System.out.println("ack(6)");
//            win.ack(7);
//            System.out.println("ack(7)");
//            win.ack(8);
//            System.out.println("ack(8)");
//            win.ack(6);
//            System.out.println("ack(6)");
//            win.ack(9);
//            System.out.println("ack(9)");
//            System.out.println(win);
//
//            Thread.sleep(5000);
//            win.ack(3);
//            System.out.println("ack(3)");
//            System.out.println(win);
//
//            Thread.sleep(3000);
//            win.add(10, new Message());
//            win.add(11, new Message());
//            System.out.println(win);
//            Thread.sleep(3000);
//            win.ack(10);
//            System.out.println("ack(10)");
//            win.ack(11);
//            System.out.println("ack(11)");
//            System.out.println(win);
//
//            win.add(12, new Message());
//            win.add(13, new Message());
//            win.add(14, new Message());
//            win.add(15, new Message());
//            win.add(16, new Message());
//            System.out.println(win);
//
//            Util.sleep(1000);
//            win.ack(12);
//            System.out.println("ack(12)");
//            win.ack(13);
//            System.out.println("ack(13)");
//
//            win.ack(15);
//            System.out.println("ack(15)");
//            System.out.println(win);
//
//            Util.sleep(5000);
//            win.ack(16);
//            System.out.println("ack(16)");
//            System.out.println(win);
//
//            Util.sleep(1000);
//
//            win.ack(14);
//            System.out.println("ack(14)");
//            System.out.println(win);
//        } catch (Exception e) {
//            log.error(e);
//        }
//    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy