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

com.gemstone.gemfire.internal.cache.InitialImageFlowControl Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.internal.cache;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import com.gemstone.gemfire.distributed.internal.DM;
import com.gemstone.gemfire.distributed.internal.DistributionAdvisor;
import com.gemstone.gemfire.distributed.internal.DistributionManager;
import com.gemstone.gemfire.distributed.internal.DistributionMessage;
import com.gemstone.gemfire.distributed.internal.MembershipListener;
import com.gemstone.gemfire.distributed.internal.ProcessorKeeper21;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.internal.DataSerializableFixedID;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.org.jgroups.util.StringId;

/**
 * This class keeps track of how many InitialImageMessages are in flight between the
 * initial image provider and the image target.
 * 
 * The provider is responsible for calling acquirePermit before sending an initial image
 * chunk. The acquire will block if two many messages are in flight.
 * 
 * The initial image target sends FlowControlPermitMessage to the image provider after
 * each processed chunk. Upon receiving the FlowControlPermit message, the provider
 * will increase the number of permits available.
 * @author dsmith
 *
 */
public class InitialImageFlowControl implements MembershipListener {
  private static final ProcessorKeeper21 keeper = new ProcessorKeeper21(false);
  private int id;
  private int maxPermits = InitialImageOperation.CHUNK_PERMITS;
  private final Semaphore permits = new Semaphore(maxPermits);
  private final DM dm;
  private final InternalDistributedMember target;
  private final AtomicBoolean aborted = new AtomicBoolean();
  
  public static InitialImageFlowControl register(DM dm, InternalDistributedMember target) {
    InitialImageFlowControl control =new InitialImageFlowControl(dm, target);
    int id = keeper.put(control);
    control.id = id;
    
    Set availableIds = dm.addMembershipListenerAndGetDistributionManagerIds(control);
    if(!availableIds.contains(target)) {
      control.abort();
    }
    return control;
  }

  private InitialImageFlowControl(DM dm, InternalDistributedMember target) {
    this.dm = dm;
    this.target = target;
  }
  
  private void releasePermit() {
    permits.release();
    incMessagesInFlight(-1);
  }
  
  private void incMessagesInFlight(int val) {
    dm.getStats().incInitialImageMessagesInFlight(val);
  }
  
  
  /**
   * Acquire a permit to send another message
   */
  public void acquirePermit() {
    long startWaitTime = System.currentTimeMillis();
    while (!aborted.get()) {
      checkCancellation();

      boolean interrupted = false;
      try {
        basicWait(startWaitTime);
        break;
      }
      catch (InterruptedException e) {
        interrupted = true; // keep looping
        checkCancellation();
      }
      finally {
        if (interrupted) {
          Thread.currentThread().interrupt();
        }
      }
    } // while
    if(!aborted.get()) {
      incMessagesInFlight(1);
    }
  }
  
  private void basicWait(long startWaitTime) throws InterruptedException {
    long timeout = getAckWaitThreshold() * 1000L;
    long timeSoFar = System.currentTimeMillis() - startWaitTime;
    if (timeout <= 0) {
      timeout = Long.MAX_VALUE;
    }
    if (!aborted.get() && !permits.tryAcquire(timeout-timeSoFar-1, TimeUnit.MILLISECONDS)) {
      checkCancellation();

      Set activeMembers = dm.getDistributionManagerIds();
      final Object[] msgArgs = new Object[] {getAckWaitThreshold(), this, dm.getId(), activeMembers};
      final StringId msg = LocalizedStrings.ReplyProcessor21_0_SEC_HAVE_ELAPSED_WHILE_WAITING_FOR_REPLIES_1_ON_2_WHOSE_CURRENT_MEMBERSHIP_LIST_IS_3;
      dm.getLoggerI18n().warning(msg, msgArgs);

      permits.acquire();
      
      // Give an info message since timeout gave a warning.
      dm.getLoggerI18n().info(LocalizedStrings.ReplyProcessor21_WAIT_FOR_REPLIES_COMPLETED);
    }
  }
      
  /**
   * Return the time in sec to wait before sending an alert while
   * waiting for ack replies.  Note that the ack wait threshold may
   * change at runtime, so we have to consult the system every time.
   */
  private int getAckWaitThreshold() {
    return dm.getConfig().getAckWaitThreshold();
  }

  private void checkCancellation() {
    dm.getCancelCriterion().checkCancelInProgress(null);
  } 

  public void unregister() {
    dm.removeMembershipListener(this);
    keeper.remove(id);
    abort();
  }
  
  public int getId() {
    return id;
  }
  
  public void memberDeparted(InternalDistributedMember id, boolean crashed) {
    if(id.equals(target)) {
      abort();
    }
    
  }

  private void abort() {
    if(!aborted.getAndSet(true)) {
      incMessagesInFlight(- (maxPermits - permits.availablePermits()));
      // Just in case java has issues with semaphores rolling over, set this
      // to half Integer.MAX_VALUE rather to release all of the waiters
      permits.release(Integer.MAX_VALUE / 2);
    }
  }

  public void memberJoined(InternalDistributedMember id) {
    //Do nothing
  }

  public void quorumLost(Set failures, List remaining) {
  }

  public void memberSuspect(InternalDistributedMember id,
      InternalDistributedMember whoSuspected) {
    //Do nothing
  }

  @Override
  public String toString() {
    return "";
  }


  public static class FlowControlPermitMessage extends DistributionMessage implements DataSerializableFixedID {
    private int keeperId;
    
    private FlowControlPermitMessage(int keeperId2) {
      this.keeperId = keeperId2;
    }

    public FlowControlPermitMessage() {
    }

    public static void send(DM dm, InternalDistributedMember recipient, int keeperId) {
      FlowControlPermitMessage message = new FlowControlPermitMessage(keeperId);
      message.setRecipient(recipient);
      dm.putOutgoing(message);
    }

    @Override
    public int getProcessorType() {
      return DistributionManager.STANDARD_EXECUTOR;
    }
    
    @Override
    public boolean getInlineProcess() {
      return true;
    }

    @Override
    protected void process(DistributionManager dm) {
      InitialImageFlowControl control = (InitialImageFlowControl) keeper.retrieve(keeperId);
      if(control != null) {
        control.releasePermit();
      }
    }

    public int getDSFID() {
      return FLOW_CONTROL_PERMIT_MESSAGE;
    }

    @Override
    public void fromData(DataInput in) throws IOException,
        ClassNotFoundException {
      super.fromData(in);
      keeperId = in.readInt();
    }

    @Override
    public void toData(DataOutput out) throws IOException {
      super.toData(out);
      out.writeInt(keeperId);
    }
    
    
    
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy