/*******************************************************************************
* 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;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import rubah.RubahThread;
import rubah.runtime.Version;
import rubah.runtime.VersionManager;
import rubah.runtime.classloader.RubahClassloader;
import rubah.runtime.state.UpdateState.StoppedThread;
import rubah.runtime.state.migrator.MigratorSubFactory;
import rubah.runtime.state.migrator.StaticFieldsMigratorFactory;
import rubah.runtime.state.migrator.MigratorSubFactory.Migrator;
import rubah.runtime.state.strategy.MigrationStrategy;
public class MigratingProgramState extends RubahState {
private final static int SAMPLE_TIME_MS = 1000;
private final static String PRINT_CONVERTED_FILE = "printConversionsToFile";
private final static File conversionsFile;
private Set<Class<?>> migratedClasses = Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>());
static {
String fileName = System.getProperty(PRINT_CONVERTED_FILE);
if (fileName != null) {
conversionsFile = new File(fileName);
} else {
conversionsFile = null;
}
}
private boolean stop = false;
private Thread printingThread = new Thread(){
@Override
public void run() {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(conversionsFile));
long conversions = strategy.countMigrated();
while (true) {
long time = System.currentTimeMillis();
try {
Thread.sleep(SAMPLE_TIME_MS);
} catch (InterruptedException e) {
continue;
}
time = System.currentTimeMillis() - time;
long newConversions = strategy.countMigrated();
int n = Math.max((int) Math.round(Math.floor(time/SAMPLE_TIME_MS)), 1);
long convs = (newConversions-conversions)/n;
for (int i = 0 ; i < n ; i++) {
writer.write(""+convs+"\n");
}
writer.flush();
conversions = newConversions;
synchronized (this) {
if (stop)
break;
}
}
writer.close();
} catch (IOException e) {
throw new Error(e);
}
}
};
private Map<RubahThread, RubahThread> redirectedThreads = new HashMap<>();
protected MigrationStrategy strategy;
public MigratingProgramState(UpdateState state) {
super(state);
}
@Override
public void doStart() {
this.strategy = this.state.getOptions().getMigrationStrategy();
this.strategy.setState(this);
this.state.setLazyStrategy(this.strategy);
if (conversionsFile != null) {
this.printingThread.start();
}
System.out.println("Traversing the heap using strategy " + this.strategy.getDescription() + "...");
long time = System.currentTimeMillis();
LinkedList<Class<?>> loadedClasses = new LinkedList<>();
synchronized (RubahClassloader.class) {
loadedClasses = new LinkedList<Class<?>>(RubahClassloader.getLoadedClasses());
}
// Start traversing the heap
try {
this.migrateStaticFields(loadedClasses);
// Threads
for (StoppedThread stoppedThread : this.state.getStopped()) {
Runnable r = (Runnable)this.strategy.migrate(stoppedThread.rubahThread.getTarget());
stoppedThread.rubahThread.setTarget(r);
}
for (Entry<RubahThread, RubahThread> entry : this.redirectedThreads.entrySet()) {
entry.getKey().setTarget(entry.getValue());
}
} catch (IllegalArgumentException e) {
throw new Error(e);
} catch (Error e) {
e.printStackTrace();
throw e;
}
this.strategy.waitForFinish();
time = System.currentTimeMillis() - time;
System.out.println(
"Converted " + this.strategy.countMigrated() + " objects in " + time + "ms");
if (this.shouldStopPrintingThread())
this.stopPrintingThread();
}
private void migrateStaticFields(Collection<Class<?>> classes) {
Version v1 = VersionManager.getInstance().getLatestVersion();
MigratorSubFactory staticMigratorFactory = new StaticFieldsMigratorFactory(this.strategy, v1);
for (Class<?> c : classes) {
if (this.migratedClasses.contains(c))
continue;
this.migratedClasses.add(c);
if (!staticMigratorFactory.canMigrate(c))
continue;
Migrator migrator = staticMigratorFactory.buildMigrator();
Object newC = migrator.migrate(c);
migrator.followReferences(newC);
}
}
@Override
public boolean isUpdating() {
return true;
}
@Override
public boolean isUpdateRequested() {
return false;
}
@Override
public void redirectThread(RubahThread t0, RubahThread t1) {
this.redirectedThreads.put(t0, t1);
}
public UpdateState getState() {
return this.state;
}
protected final void stopPrintingThread() {
if (conversionsFile == null)
return;
synchronized (this.printingThread) {
this.stop = true;
}
}
protected boolean shouldStopPrintingThread() {
return true;
}
@Override
public void ensureStaticFieldsMigrated(Class<?> c) {
this.migrateStaticFields(Arrays.asList(new Class<?>[]{ c }));
}
}