/*******************************************************************************
* Copyright 2014,
* Luis Pina <luis@luispina.me>,
* Michael Hicks <mwh@cs.umd.edu>
*
* This file is part of Rubah.
*
* Rubah is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rubah is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rubah. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package rubah.runtime.state.strategy;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.javatuples.Quartet;
import rubah.runtime.state.MigratingProgramState;
/*default*/ abstract class ExecutorStrategy extends SingleThreaded {
private transient ConcurrentLinkedDeque<Quartet<Object, Long, Object, Long>> queued = new ConcurrentLinkedDeque<>();
protected transient StrippedCounter inFlight;
protected transient ExecutorService executor;
protected final int nThreads;
public ExecutorStrategy(MappingStrategy mapping, int nThreads) {
super(mapping);
this.nThreads = nThreads;
}
@Override
public MigrationStrategy setState(MigratingProgramState state) {
this.queued = new ConcurrentLinkedDeque<>();
this.inFlight = new StrippedCounter(this);
return super.setState(state);
}
protected abstract ExecutorService getExecutor();
@Override
protected void migrateStatic(Object base, long offset) {
super.migrate(base, offset, base, offset);
}
@Override
public void migrate(final Object fromBase, long fromOffset, final Object toBase, long toOffset) {
if (this.executor == null) {
this.queued.add(new Quartet<Object, Long, Object, Long>(fromBase, fromOffset, toBase, toOffset));
return;
}
this.inFlight.increment();
final short fromOff = (short) fromOffset;
final short toOff = (short) toOffset;
this.executor.execute(new Runnable() {
@Override
public void run() {
try {
ExecutorStrategy.super.doMigrate(
fromBase,
fromOff,
unsafe.getObject(fromBase, (long) fromOff),
toBase,
toOff,
unsafe.getObject(toBase, (long) toOff));
} catch (Throwable e) {
System.out.println(e);
e.printStackTrace();
throw new Error(e);
}
inFlight.decrement();
}
});
}
protected void beforeShutdown() {
// Empty
}
@Override
public void waitForFinish() {
this.executor = this.getExecutor();
while (!this.queued.isEmpty()) {
Quartet<Object, Long, Object, Long> el = this.queued.poll();
this.migrate(el.getValue0(), el.getValue1(), el.getValue2(), el.getValue3());
}
synchronized (this) {
while (this.inFlight.get() != 0) {
try {
this.wait();
} catch (InterruptedException e) {
continue;
}
}
}
this.beforeShutdown();
this.executor.shutdown();
boolean finished = false;
do {
try {
finished = this.executor.awaitTermination(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
continue;
}
} while (!finished);
}
@Override
public String getDescription() {
return super.getDescription() + " - " + this.nThreads + " threads";
}
private static class StrippedCounter {
private static final int MIN_THRESHOLD = 50;
private Object notify;
private LinkedList<Strip> strips = new LinkedList<Strip>();
private ThreadLocal<Strip> counter = new ThreadLocal<Strip>() {
@Override
protected Strip initialValue() {
Strip ret = new Strip();
synchronized (strips) {
strips.add(ret);
}
return ret;
}
};
public StrippedCounter(Object notify) {
this.notify = notify;
}
public void increment() {
Strip s = this.counter.get();
s.lock.lock();
try {
s.value++;
} finally {
s.lock.unlock();
}
}
public void decrement() {
Strip s = this.counter.get();
int newValue;
s.lock.lock();
try {
newValue = --(s.value);
} finally {
s.lock.unlock();
}
if (newValue <= 0) {
if (this.redistribute() == 0) {
synchronized (notify) {
notify.notifyAll();
}
}
}
}
private int redistribute() {
int ret = 0;
synchronized (strips) {
for (Strip strip : strips) {
strip.lock.lock();
}
for (Strip strip : strips) {
ret += strip.value;
strip.value = 0;
}
if (ret < MIN_THRESHOLD) {
this.counter.get().value = ret;
} else {
int newVal = ret / strips.size();
int lastVal = ret - (newVal * (strips.size() - 1));
for (Strip strip : strips) {
if (strip == strips.getLast())
strip.value = lastVal;
else
strip.value = newVal;
}
}
for (Strip strip : strips) {
strip.lock.unlock();
}
}
return ret;
}
public int get() {
int ret = 0;
synchronized (strips) {
for (Strip strip : strips) {
strip.lock.lock();
}
for (Strip strip : strips) {
ret += strip.value;
strip.lock.unlock();
}
}
return ret;
}
private static class Strip {
int value = 0;
Lock lock = new ReentrantLock();
}
}
}