/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: EThread.java * * Copyright (c) 2003 Sun Microsystems and Static Free Software * * Electric(tm) 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. * * Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool; import com.sun.electric.StartupPrefs; import com.sun.electric.Main; import com.sun.electric.database.EditingPreferences; import com.sun.electric.database.Environment; import com.sun.electric.database.Snapshot; import com.sun.electric.database.change.Undo; import com.sun.electric.database.constraint.Constraints; import com.sun.electric.database.hierarchy.EDatabase; import com.sun.electric.database.variable.UserInterface; import com.sun.electric.tool.user.ActivityLogger; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.logging.Level; /** * Thread for execution Jobs in Electric. */ class EThread extends Thread { private static final String CLASS_NAME = EThread.class.getName(); private static final ArrayList<Snapshot> snapshotCache = new ArrayList<Snapshot>(); private static int maximumSnapshots = StartupPrefs.getMaxUndoHistory(); /** EJob which Thread is executing now. */ EJob ejob; /** True if this EThread is execution server job. */ boolean isServerThread; /* Database in which thread is executing. */ EDatabase database; ServerJobManager.UserInterfaceRedirect userInterface; /** Creates a new instance of EThread */ EThread(int id) { super("EThread-" + id); // setUserInterface(Job.currentUI); Job.logger.logp(Level.FINER, CLASS_NAME, "constructor", getName()); start(); } EThread(String name) { super(name); } public void run() { Job.logger.logp(Level.FINE, CLASS_NAME, "run", getName()); EJob finishedEJob = null; for (;;) { ejob = Job.serverJobManager.selectEJob(finishedEJob); Job.logger.logp(Level.FINER, CLASS_NAME, "run", "selectedJob {0}", ejob.jobName); isServerThread = ejob.jobType != Job.Type.CLIENT_EXAMINE; database = isServerThread ? EDatabase.serverDatabase() : EDatabase.clientDatabase(); ejob.changedFields = new ArrayList<Field>(); // Throwable jobException = null; Environment.setThreadEnvironment(database.getEnvironment()); EditingPreferences.setThreadEditingPreferences(ejob.editingPreferences); userInterface = new ServerJobManager.UserInterfaceRedirect(ejob.jobKey); database.lock(!ejob.isExamine()); ejob.oldSnapshot = database.backup(); try { if (ejob.jobType != Job.Type.CLIENT_EXAMINE && !ejob.jobKey.startedByServer()) { Throwable e = ejob.deserializeToServer(); if (e != null) { throw e; } } switch (ejob.jobType) { case CHANGE: database.lowLevelBeginChanging(ejob.serverJob.tool); Constraints.getCurrent().startBatch(ejob.oldSnapshot); userInterface.setCurrents(ejob.serverJob); if (!ejob.serverJob.doIt()) { throw new JobException("Job '" + ejob.jobName + "' failed"); } Constraints.getCurrent().endBatch(ejob.client.userName); database.lowLevelEndChanging(); ejob.newSnapshot = database.backup(); break; case UNDO: database.lowLevelSetCanUndoing(true); // userInterface.curTechId = null; // userInterface.curLibId = null; // userInterface.curCellId = null; int snapshotId = ((Undo.UndoJob) ejob.serverJob).getSnapshotId(); Snapshot undoSnapshot = findInCache(snapshotId); if (undoSnapshot == null) { throw new JobException("Snapshot " + snapshotId + " not found"); } database.undo(undoSnapshot); database.lowLevelSetCanUndoing(false); break; case SERVER_EXAMINE: userInterface.setCurrents(ejob.serverJob); if (!ejob.serverJob.doIt()) { throw new JobException("Job '" + ejob.jobName + "' failed"); } break; case CLIENT_EXAMINE: if (ejob.jobKey.startedByServer()) { Throwable e = ejob.deserializeToClient(); if (e != null) { throw e; } } userInterface.setCurrents(ejob.clientJob); if (!ejob.clientJob.doIt()) { throw new JobException("Job '" + ejob.jobName + "' failed"); } break; } ejob.serializeResult(database); ejob.newSnapshot = database.backup(); // database.checkFresh(ejob.newSnapshot); // ejob.state = EJob.State.SERVER_DONE; } catch (Throwable e) { // Batch mode is used from scripts in which it is VERY // important for the JVM to exit with a nonzero error // code whenever something goes wrong. e.getStackTrace(); if (!(e instanceof JobException)) { e.printStackTrace(); } if (!ejob.isExamine()) { recoverDatabase(e instanceof JobException); database.lowLevelEndChanging(); database.lowLevelSetCanUndoing(false); } ejob.serializeExceptionResult(e, database); // ejob.state = EJob.State.SERVER_FAIL; } finally { database.unlock(); userInterface = null; Environment.setThreadEnvironment(null); EditingPreferences.setThreadEditingPreferences(null); } putInCache(ejob.oldSnapshot, ejob.newSnapshot); finishedEJob = ejob; ejob = null; isServerThread = false; database = null; Job.logger.logp(Level.FINER, CLASS_NAME, "run", "finishedJob {0}", finishedEJob.jobName); } } private void recoverDatabase(boolean quick) { database.lowLevelSetCanUndoing(true); try { if (quick) { database.undo(ejob.oldSnapshot); } else { database.recover(ejob.oldSnapshot); } ejob.newSnapshot = ejob.oldSnapshot; return; } catch (Throwable e) { ActivityLogger.logException(e); } for (;;) { try { Snapshot snapshot = findValidSnapshot(); database.recover(snapshot); ejob.newSnapshot = snapshot; return; } catch (Throwable e) { ActivityLogger.logException(e); } } } UserInterface getUserInterface() { return userInterface; } /** * Find some valid snapshot in cache. */ static Snapshot findValidSnapshot() { for (;;) { Snapshot snapshot; synchronized (snapshotCache) { if (snapshotCache.isEmpty()) { return EDatabase.serverDatabase().getInitialSnapshot(); } snapshot = snapshotCache.remove(snapshotCache.size() - 1); } try { snapshot.check(); return snapshot; } catch (Throwable e) { ActivityLogger.logException(e); } } } private static Snapshot findInCache(int snapshotId) { synchronized (snapshotCache) { for (int i = snapshotCache.size() - 1; i >= 0; i--) { Snapshot snapshot = snapshotCache.get(i); if (snapshot.snapshotId == snapshotId) { return snapshot; } } } return null; } private static void putInCache(Snapshot oldSnapshot, Snapshot newSnapshot) { synchronized (snapshotCache) { if (!snapshotCache.contains(newSnapshot)) { while (!snapshotCache.isEmpty() && snapshotCache.get(snapshotCache.size() - 1) != oldSnapshot) { snapshotCache.remove(snapshotCache.size() - 1); } snapshotCache.add(newSnapshot); } while (snapshotCache.size() > maximumSnapshots) { snapshotCache.remove(0); } } } /** * Method to set the size of the history list and return the former size. * @param newSize the new size of the history list (number of batches of changes). * If not positive, the list size is not changed. * @return the former size of the history list. */ public static int setHistoryListSize(int newSize) { if (newSize <= 0) { return maximumSnapshots; } int oldSize = maximumSnapshots; maximumSnapshots = newSize; while (snapshotCache.size() > maximumSnapshots) { snapshotCache.remove(0); } return oldSize; } /** * If this EThread is running a Job return it. * Return null otherwise. * @return a running Job or null */ Job getRunningJob() { if (ejob == null) { return null; } return ejob.jobType == Job.Type.CLIENT_EXAMINE ? ejob.clientJob : ejob.serverJob; } EJob getRunningEJob() { return ejob; } }