blob: 35bbbed8dad969509a42f8590f0d3d51d2c1d002 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.ignite.internal;
import org.apache.ignite.internal.util.*;
import org.apache.ignite.internal.util.tostring.*;
import org.apache.ignite.internal.util.typedef.internal.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
*
*/
@GridToStringExclude
public class GridKernalGatewayImpl implements GridKernalGateway, Serializable {
/** */
private static final long serialVersionUID = 0L;
/** */
@GridToStringExclude
private final GridSpinReadWriteLock rwLock = new GridSpinReadWriteLock();
/** */
@GridToStringExclude
private final Collection<Runnable> lsnrs = new GridSetWrapper<>(new IdentityHashMap<Runnable, Object>());
/** */
private volatile GridKernalState state = GridKernalState.STOPPED;
/** */
@GridToStringExclude
private final String gridName;
/**
* User stack trace.
*
* Intentionally uses non-volatile variable for optimization purposes.
*/
private String stackTrace;
/**
* @param gridName Grid name.
*/
public GridKernalGatewayImpl(String gridName) {
this.gridName = gridName;
}
/** {@inheritDoc} */
@Override public void lightCheck() throws IllegalStateException {
if (state != GridKernalState.STARTED)
throw illegalState();
}
/** {@inheritDoc} */
@SuppressWarnings({"LockAcquiredButNotSafelyReleased", "BusyWait"})
@Override public void readLock() throws IllegalStateException {
if (stackTrace == null)
stackTrace = stackTrace();
rwLock.readLock();
if (state != GridKernalState.STARTED) {
// Unlock just acquired lock.
rwLock.readUnlock();
throw illegalState();
}
}
/** {@inheritDoc} */
@Override public void readLockAnyway() {
if (stackTrace == null)
stackTrace = stackTrace();
rwLock.readLock();
}
/** {@inheritDoc} */
@Override public void readUnlock() {
rwLock.readUnlock();
}
/** {@inheritDoc} */
@SuppressWarnings({"BusyWait"})
@Override public void writeLock() {
if (stackTrace == null)
stackTrace = stackTrace();
boolean interrupted = false;
// Busy wait is intentional.
while (true)
try {
if (rwLock.tryWriteLock(200, TimeUnit.MILLISECONDS))
break;
else
Thread.sleep(200);
}
catch (InterruptedException ignore) {
// Preserve interrupt status & ignore.
// Note that interrupted flag is cleared.
interrupted = true;
}
if (interrupted)
Thread.currentThread().interrupt();
}
/** {@inheritDoc} */
@Override public boolean tryWriteLock(long timeout) throws InterruptedException {
boolean acquired = rwLock.tryWriteLock(timeout, TimeUnit.MILLISECONDS);
if (acquired) {
if (stackTrace == null)
stackTrace = stackTrace();
return true;
}
return false;
}
/**
* Retrieves user stack trace.
*
* @return User stack trace.
*/
private static String stackTrace() {
StringWriter sw = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(sw));
return sw.toString();
}
/**
* Creates new illegal state exception.
*
* @return Newly created exception.
*/
private IllegalStateException illegalState() {
return new IllegalStateException("Grid is in invalid state to perform this operation. " +
"It either not started yet or has already being or have stopped [gridName=" + gridName +
", state=" + state + ']');
}
/** {@inheritDoc} */
@Override public void writeUnlock() {
rwLock.writeUnlock();
}
/** {@inheritDoc} */
@Override public void setState(GridKernalState state) {
assert state != null;
// NOTE: this method should always be called within write lock.
this.state = state;
if (state == GridKernalState.STOPPING) {
Runnable[] runs;
synchronized (lsnrs) {
lsnrs.toArray(runs = new Runnable[lsnrs.size()]);
}
// In the same thread.
for (Runnable r : runs)
r.run();
}
}
/** {@inheritDoc} */
@Override public GridKernalState getState() {
return state;
}
/** {@inheritDoc} */
@Override public void addStopListener(Runnable lsnr) {
assert lsnr != null;
if (state == GridKernalState.STARTING || state == GridKernalState.STARTED)
synchronized (lsnrs) {
lsnrs.add(lsnr);
}
else
// Call right away in the same thread.
lsnr.run();
}
/** {@inheritDoc} */
@Override public void removeStopListener(Runnable lsnr) {
assert lsnr != null;
synchronized (lsnrs) {
lsnrs.remove(lsnr);
}
}
/** {@inheritDoc} */
@Override public String userStackTrace() {
return stackTrace;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridKernalGatewayImpl.class, this);
}
}