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);
}
}
}