com.gemstone.gemfire.distributed.internal.locks.DLockReleaseProcessor Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* 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.distributed.internal.locks;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.InternalGemFireError;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.distributed.LockServiceDestroyedException;
import com.gemstone.gemfire.distributed.internal.*;
import com.gemstone.gemfire.distributed.internal.membership.*;
import java.io.*;
/**
* Synchronously releases a lock.
*
* @author Kirk Lund
*/
public class DLockReleaseProcessor
extends ReplyProcessor21 {
private DLockReleaseReplyMessage reply;
protected LogWriterI18n log;
protected Object objectName;
public DLockReleaseProcessor(DM dm,
InternalDistributedMember member,
String serviceName,
Object objectName) {
super(dm, member);
this.log = new DLockLogWriter(serviceName, dm.getLoggerI18n());
this.objectName = objectName;
}
/** Returns true if release was acknowledged by the grantor; false means
* we targeted someone who is not the grantor */
protected boolean release(InternalDistributedMember grantor,
String serviceName,
boolean lockBatch,
int lockId) {
DM dm = getDistributionManager();
DLockReleaseMessage msg = new DLockReleaseMessage();
msg.processorId = getProcessorId();
msg.serviceName = serviceName;
msg.objectName = this.objectName;
msg.lockBatch = lockBatch;
msg.lockId = lockId;
msg.setRecipient(grantor);
if (grantor.equals(dm.getId())) {
// local... don't message...
msg.setSender(grantor);
msg.processLocally(dm);
}
else {
dm.putOutgoing(msg);
}
// keep waiting even if interrupted
try {
waitForRepliesUninterruptibly();
}
catch (ReplyException e) {
e.handleAsUnexpected();
}
if (this.reply == null) return false;
return this.reply.replyCode == DLockReleaseReplyMessage.OK;
}
@Override
protected boolean allowReplyFromSender() {
return true;
}
@Override
public void process(DistributionMessage msg) {
try {
Assert.assertTrue(msg instanceof DLockReleaseReplyMessage,
"DLockReleaseProcessor is unable to process message of type " +
msg.getClass());
DLockReleaseReplyMessage myReply = (DLockReleaseReplyMessage) msg;
if (this.log.fineEnabled()) {
this.log.fine("Handling: " + myReply);
}
this.reply = myReply;
// grantor acknowledged release of lock...
if (myReply.replyCode == DLockReleaseReplyMessage.OK) {
if (this.log.fineEnabled()) {
this.log.fine("Successfully released " + this.objectName +
" in " + myReply.serviceName);
}
}
// sender denies being the grantor...
else if (myReply.replyCode == DLockReleaseReplyMessage.NOT_GRANTOR) {
if (this.log.fineEnabled()) {
this.log.fine(myReply.getSender() +
" has responded DLockReleaseReplyMessage.NOT_GRANTOR for " +
myReply.serviceName);
}
}
}
finally {
super.process(msg);
/*if (this.log.fineEnabled()) {
this.log.fine("Finished handling: " + msg);
}*/
}
}
// -------------------------------------------------------------------------
// DLockReleaseMessage
// -------------------------------------------------------------------------
public static final class DLockReleaseMessage
extends HighPriorityDistributionMessage
implements MessageWithReply {
/** The name of the DistributedLockService */
protected String serviceName;
/** The object name */
protected Object objectName;
/** True if objectName identifies a batch of locks */
protected boolean lockBatch;
/** Id of the processor that will handle replies */
protected int processorId;
/** Matches up this release with the original lock request */
protected int lockId;
// set used during processing of this msg...
protected DLockService svc;
protected DLockGrantor grantor;
public DLockReleaseMessage() {}
@Override
public int getProcessorId() {
return this.processorId;
}
/**
* Processes this message - invoked on the node that is the lock grantor.
*/
@Override
protected void process(final DistributionManager dm) {
boolean failed = true;
ReplyException replyException = null;
try {
this.svc = DLockService.getInternalServiceNamed(this.serviceName);
if (this.svc == null) {
failed = false; // basicProcess has it's own finally-block w reply
basicProcess(dm, false); // don't have a grantor anymore
}
else {
executeBasicProcess(dm); // use executor
}
failed = false; // nothing above threw anything
}
catch (RuntimeException e) {
replyException = new ReplyException(e);
throw e;
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
replyException = new ReplyException(e);
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (see above). However, there is
SystemFailure.checkFailure();
replyException = new ReplyException(e);
throw e;
}
finally {
if (failed) {
// above code failed so now ensure reply is sent
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm,
"DLockReleaseMessage.process failed for <" + this + ">");
}
int replyCode = DLockReleaseReplyMessage.NOT_GRANTOR;
DLockReleaseReplyMessage replyMsg = new DLockReleaseReplyMessage();
replyMsg.serviceName = this.serviceName;
replyMsg.replyCode = replyCode;
replyMsg.setProcessorId(this.processorId);
replyMsg.setRecipient(getSender());
replyMsg.setException(replyException);
if (dm.getId().equals(getSender())) {
replyMsg.setSender(getSender());
replyMsg.dmProcess(dm);
}
else {
dm.putOutgoing(replyMsg);
}
}
}
}
/** Process locally without using messaging or executor */
protected void processLocally(final DM dm) {
this.svc = DLockService.getInternalServiceNamed(this.serviceName);
basicProcess(dm, true); // don't use executor
}
/**
* Execute basicProcess inside Pooled Executor because grantor may not
* be initializing which will require us to wait.
*
* this.svc and this.grantor must be set before calling this method.
*/
private void executeBasicProcess(final DM dm) {
final DLockReleaseMessage msg = this;
dm.getWaitingThreadPool().execute(new Runnable() {
public void run() {
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[executeBasicProcess] waitForGrantor " + msg);
}
basicProcess(dm, true);
}
});
}
/**
* Perform basic processing of this message.
*
* this.svc and this.grantor must be set before calling this method.
*/
protected void basicProcess(final DM dm, final boolean waitForGrantor) {
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[basicProcess] " + this);
}
int replyCode = DLockReleaseReplyMessage.NOT_GRANTOR;
ReplyException replyException = null;
try {
if (svc == null || svc.isDestroyed()) return;
if (waitForGrantor) {
try {
this.grantor = DLockGrantor.waitForGrantor(this.svc);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
this.grantor = null;
}
}
if (grantor == null || grantor.isDestroyed()) {
return;
}
try {
if (lockBatch) {
throw new InternalGemFireError("unexpected call");
//grantor.releaseLockBatch(objectName, getSender());
//replyCode = DLockReleaseReplyMessage.OK;
}
else {
grantor.releaseIfLocked(objectName, getSender(), lockId);
replyCode = DLockReleaseReplyMessage.OK;
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
replyCode = DLockReleaseReplyMessage.NOT_GRANTOR;
}
}
catch (LockGrantorDestroyedException ignore) {
}
catch (LockServiceDestroyedException ignore) {
}
catch (RuntimeException e) {
replyException = new ReplyException(e);
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[basicProcess] caught RuntimeException", e);
}
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
replyException = new ReplyException(e);
if (DLockLogWriter.fineEnabled(dm)) {
DLockLogWriter.fine(dm, "[basicProcess] caught Error", e);
}
}
finally {
DLockReleaseReplyMessage replyMsg = new DLockReleaseReplyMessage();
replyMsg.serviceName = this.serviceName;
replyMsg.replyCode = replyCode;
replyMsg.setProcessorId(this.processorId);
replyMsg.setRecipient(getSender());
replyMsg.setException(replyException);
if (dm.getId().equals(getSender())) {
replyMsg.setSender(getSender());
replyMsg.dmProcess(dm);
}
else {
dm.putOutgoing(replyMsg);
}
if (grantor != null && !lockBatch) {
// moved what was here into grantor...
try {
grantor.postRemoteReleaseLock(objectName);
}
catch (InterruptedException e) {
try {
dm.getCancelCriterion().checkCancelInProgress(e);
} finally {
Thread.currentThread().interrupt();
}
}
} // grantor != null
else {
if (DLockGrantor.DEBUG_SUSPEND_LOCK) {
dm.getLoggerI18n().fine(
"DLockReleaseMessage, omitted postRemoteRelease lock on "
+ objectName + "; grantor = " + grantor
+ ", lockBatch = " + lockBatch + ", replyMsg = " + replyMsg);
}
}
}
}
public int getDSFID() {
return DLOCK_RELEASE_MESSAGE;
}
@Override
public void toData(DataOutput out) throws IOException {
super.toData(out);
DataSerializer.writeString(this.serviceName, out);
DataSerializer.writeObject(this.objectName, out);
out.writeBoolean(this.lockBatch);
out.writeInt(this.processorId);
out.writeInt(this.lockId);
}
@Override
public void fromData(DataInput in)
throws IOException, ClassNotFoundException {
super.fromData(in);
this.serviceName = DataSerializer.readString(in);
this.objectName = DataSerializer.readObject(in);
this.lockBatch = in.readBoolean();
this.processorId = in.readInt();
this.lockId = in.readInt();
}
@Override
public String toString() {
return "DLockReleaseMessage for " +
this.serviceName + ", " + this.objectName +
"; processorId=" + this.processorId +
"; lockBatch=" + this.lockBatch +
"; lockId=" + this.lockId;
}
} // DLockReleaseMessage
// -------------------------------------------------------------------------
// DLockReleaseReplyMessage
// -------------------------------------------------------------------------
public static final class DLockReleaseReplyMessage
extends ReplyMessage {
static final int NOT_GRANTOR = 0;
static final int OK = 1;
/** Name of service to release the lock in; for toString only */
protected String serviceName;
/** OK or NOT_GRANTOR for the service */
protected int replyCode = NOT_GRANTOR;
@Override
public int getDSFID() {
return DLOCK_RELEASE_REPLY;
}
@Override
public void fromData(DataInput in)
throws IOException, ClassNotFoundException {
super.fromData(in);
this.serviceName = DataSerializer.readString(in);
this.replyCode = in.readInt();
}
@Override
public void toData(DataOutput out) throws IOException {
super.toData(out);
DataSerializer.writeString(this.serviceName, out);
out.writeInt(this.replyCode);
}
@Override
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("DLockReleaseReplyMessage");
buff.append(" (serviceName=");
buff.append(this.serviceName);
buff.append("; replyCode=");
switch (this.replyCode) {
case NOT_GRANTOR: buff.append("NOT_GRANTOR"); break;
case OK: buff.append("OK"); break;
default: buff.append(String.valueOf(this.replyCode)); break;
}
buff.append("; sender=");
buff.append(getSender());
buff.append("; processorId=");
buff.append(super.processorId);
buff.append(")");
return buff.toString();
}
} // DLockReleaseReplyMessage
}