diff --git a/.gitattributes b/.gitattributes index ee47b168415..3ad89dd1e53 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13699,6 +13699,7 @@ src/main/java/forge/Command.java svneol=native#text/plain src/main/java/forge/CommandList.java svneol=native#text/plain src/main/java/forge/Constant.java svneol=native#text/plain src/main/java/forge/CounterType.java svneol=native#text/plain +src/main/java/forge/FThreads.java -text src/main/java/forge/GameEntity.java -text src/main/java/forge/GameLog.java -text src/main/java/forge/ImageCache.java svneol=native#text/plain @@ -14083,6 +14084,7 @@ src/main/java/forge/control/input/InputAttack.java svneol=native#text/plain src/main/java/forge/control/input/InputBlock.java svneol=native#text/plain src/main/java/forge/control/input/InputCleanup.java svneol=native#text/plain src/main/java/forge/control/input/InputControl.java svneol=native#text/plain +src/main/java/forge/control/input/InputLockUI.java -text src/main/java/forge/control/input/InputMulligan.java svneol=native#text/plain src/main/java/forge/control/input/InputPassPriority.java svneol=native#text/plain src/main/java/forge/control/input/InputPayDiscardCost.java -text diff --git a/src/main/java/forge/FThreads.java b/src/main/java/forge/FThreads.java new file mode 100644 index 00000000000..f35a94923bd --- /dev/null +++ b/src/main/java/forge/FThreads.java @@ -0,0 +1,109 @@ +package forge; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.swing.SwingUtilities; + +import forge.control.input.InputLockUI; + +/** + * TODO: Write javadoc for this type. + * + */ +public class FThreads { + + static { + System.out.printf("(FThreads static ctor): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() ); + } + + private final static ExecutorService threadPool = Executors.newCachedThreadPool(); + public static ExecutorService getCachedPool() { + return threadPool; + } + + // This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5 + public final static ExecutorService getComputingPool(float loadFactor) { + return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor))); + } + + public static boolean isMultiCoreSystem() { + return Runtime.getRuntime().availableProcessors() > 1; + } + + /** Checks if calling method uses event dispatch thread. + * Exception thrown if method is on "wrong" thread. + * A boolean is passed to indicate if the method must be EDT or not. + * + * @param methodName   String, part of the custom exception message. + * @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT + */ + public static void checkEDT(final String methodName, final boolean mustBeEDT) { + boolean isEDT = SwingUtilities.isEventDispatchThread(); + if ( isEDT != mustBeEDT ) { + String modalOperator = mustBeEDT ? " must be" : " may not be"; + throw new IllegalStateException( methodName + modalOperator + " accessed from the event dispatch thread."); + } + } + + /** + * TODO: Write javadoc for this method. + * @param runnable + */ + public static void invokeInEDT(Runnable runnable) { + SwingUtilities.invokeLater(runnable); + } + + /** + * Invoke the given Runnable in an Event Dispatch Thread and wait for it to + * finish; but try to use SwingUtilities.invokeLater instead whenever + * feasible. + * + * Exceptions generated by SwingUtilities.invokeAndWait (if used), are + * rethrown as RuntimeExceptions. + * + * @param proc + * the Runnable to run + * @see javax.swing.SwingUtilities#invokeLater(Runnable) + */ + public static void invokeInEDTAndWait(final Runnable proc) { + if (SwingUtilities.isEventDispatchThread()) { + // Just run in the current thread. + proc.run(); + } else { + try { + SwingUtilities.invokeAndWait(proc); + } catch (final InterruptedException exn) { + throw new RuntimeException(exn); + } catch (final InvocationTargetException exn) { + throw new RuntimeException(exn); + } + } + } + + + public static void invokeInNewThread(Runnable proc) { + invokeInNewThread(proc, false); + } + + private final static InputLockUI inpuptLock = new InputLockUI(); + public static void invokeInNewThread(final Runnable proc, boolean lockUI) { + Runnable toRun = proc; + if( lockUI ) { + // checkEDT("FThreads.invokeInNewthread", true) + Singletons.getModel().getMatch().getInput().setInput(inpuptLock); + toRun = new Runnable() { + @Override + public void run() { + proc.run(); + // may try special unlock method here + Singletons.getModel().getMatch().getInput().resetInput(); + } + }; + } + + getCachedPool().execute(toRun); + } + +} diff --git a/src/main/java/forge/card/cardfactory/CardStorageReader.java b/src/main/java/forge/card/cardfactory/CardStorageReader.java index e75d1b2fc24..d284f15e64b 100644 --- a/src/main/java/forge/card/cardfactory/CardStorageReader.java +++ b/src/main/java/forge/card/cardfactory/CardStorageReader.java @@ -39,9 +39,9 @@ import javax.swing.SwingUtilities; import org.apache.commons.lang.time.StopWatch; +import forge.FThreads; import forge.card.CardRules; import forge.card.CardRulesReader; -import forge.control.FControl; import forge.error.BugReporter; import forge.gui.toolbox.FProgressBar; import forge.util.FileUtil; @@ -62,7 +62,7 @@ public class CardStorageReader { /** Default charset when loading from files. */ public static final String DEFAULT_CHARSET_NAME = "US-ASCII"; - final private boolean useThreadPool = FControl.isMultiCoreSystem(); + final private boolean useThreadPool = FThreads.isMultiCoreSystem(); final private int NUMBER_OF_PARTS = 25; final private CountDownLatch cdl = new CountDownLatch(NUMBER_OF_PARTS); @@ -209,7 +209,7 @@ public class CardStorageReader { try { if ( useThreadPool ) { - final ExecutorService executor = FControl.getComputingPool(0.5f); + final ExecutorService executor = FThreads.getComputingPool(0.5f); final List>> parts = executor.invokeAll(tasks); executor.shutdown(); cdl.await(); diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index c12b26f0b37..c6922f5cc6e 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -25,9 +25,6 @@ import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - import javax.swing.ImageIcon; import javax.swing.JLayeredPane; import javax.swing.SwingUtilities; @@ -85,14 +82,8 @@ public enum FControl { DRAFTING_PROCESS } - private final ExecutorService threadPool = Executors.newCachedThreadPool(); - private final SoundSystem soundSystem = new SoundSystem(); - - static { - System.out.printf("(FControl static ctor): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() ); - } /** *

* FControl. @@ -317,21 +308,4 @@ public enum FControl { public SoundSystem getSoundSystem() { return soundSystem; } - - public ExecutorService getThreadPool() { - return threadPool; - } - - // This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5 - public final static ExecutorService getComputingPool(float loadFactor) { - return Executors.newFixedThreadPool((int)(Runtime.getRuntime().availableProcessors() / (1-loadFactor))); - } - - /** - * TODO: Write javadoc for this method. - * @return - */ - public static boolean isMultiCoreSystem() { - return Runtime.getRuntime().availableProcessors() > 1; - } } diff --git a/src/main/java/forge/control/input/InputLockUI.java b/src/main/java/forge/control/input/InputLockUI.java new file mode 100644 index 00000000000..cd76856a653 --- /dev/null +++ b/src/main/java/forge/control/input/InputLockUI.java @@ -0,0 +1,18 @@ +package forge.control.input; + +import forge.gui.match.CMatchUI; +import forge.view.ButtonUtil; + +/** + * TODO: Write javadoc for this type. + * + */ +public class InputLockUI extends Input { + private static final long serialVersionUID = 5777143577098597374L; + + public void showMessage() { + ButtonUtil.disableAll(); + CMatchUI.SINGLETON_INSTANCE.showMessage("Waiting for actions..."); + } + +} diff --git a/src/main/java/forge/gui/GuiUtils.java b/src/main/java/forge/gui/GuiUtils.java index c93d287bff5..e8662c774cd 100644 --- a/src/main/java/forge/gui/GuiUtils.java +++ b/src/main/java/forge/gui/GuiUtils.java @@ -31,7 +31,6 @@ import java.util.List; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; import forge.Card; import forge.gui.match.VMatchUI; @@ -89,26 +88,6 @@ public final class GuiUtils { return ttf; } - /** Checks if calling method uses event dispatch thread. - * Exception thrown if method is on "wrong" thread. - * A boolean is passed to indicate if the method must be EDT or not. - * - * @param methodName   String, part of the custom exception message. - * @param mustBeEDT   boolean: true = exception if not EDT, false = exception if EDT - */ - public static void checkEDT(final String methodName, final boolean mustBeEDT) { - boolean isEDT = SwingUtilities.isEventDispatchThread(); - - if (!isEDT && mustBeEDT) { - throw new IllegalStateException( - methodName + " must be accessed from the event dispatch thread."); - } - else if (isEDT && !mustBeEDT) { - throw new IllegalStateException( - methodName + " may not be accessed from the event dispatch thread."); - } - } - /** * Clear all visually highlighted card panels on the battlefield. */ diff --git a/src/main/java/forge/gui/toolbox/FProgressBar.java b/src/main/java/forge/gui/toolbox/FProgressBar.java index bff0f133454..22015d5d7e0 100644 --- a/src/main/java/forge/gui/toolbox/FProgressBar.java +++ b/src/main/java/forge/gui/toolbox/FProgressBar.java @@ -5,7 +5,7 @@ import java.util.Date; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; -import forge.gui.GuiUtils; +import forge.FThreads; /** * A simple progress bar component using the Forge skin. @@ -37,7 +37,7 @@ public class FProgressBar extends JProgressBar { * @param s0   A description to prepend before statistics. */ public void setDescription(final String s0) { - GuiUtils.checkEDT("FProgressBar$setDescription", true); + FThreads.checkEDT("FProgressBar$setDescription", true); this.desc = s0; this.setString(s0); } @@ -77,7 +77,7 @@ public class FProgressBar extends JProgressBar { /** Resets the various values required for this class. Must be called from EDT. */ public void reset() { - GuiUtils.checkEDT("FProgressBar$reset", true); + FThreads.checkEDT("FProgressBar$reset", true); this.setIndeterminate(true); this.setValue(0); this.tempVal = 0; diff --git a/src/main/java/forge/gui/toolbox/FSkin.java b/src/main/java/forge/gui/toolbox/FSkin.java index 12d707febad..88f723e3f15 100644 --- a/src/main/java/forge/gui/toolbox/FSkin.java +++ b/src/main/java/forge/gui/toolbox/FSkin.java @@ -37,6 +37,7 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.LineBorder; +import forge.FThreads; import forge.gui.GuiUtils; import forge.view.FView; @@ -419,7 +420,7 @@ public enum FSkin { */ public static void loadLight(final String skinName) { // No need for this method to be loaded while on the EDT. - GuiUtils.checkEDT("FSkin$constructor", false); + FThreads.checkEDT("FSkin$constructor", false); // Non-default (preferred) skin name and dir. FSkin.preferredName = skinName.toLowerCase().replace(' ', '_'); @@ -479,7 +480,7 @@ public enum FSkin { */ public static void loadFull() { // No need for this method to be loaded while on the EDT. - GuiUtils.checkEDT("FSkin$load", false); + FThreads.checkEDT("FSkin$load", false); // Preferred skin name must be called via loadLight() method, // which does some cleanup and init work. diff --git a/src/main/java/forge/model/FModel.java b/src/main/java/forge/model/FModel.java index 56a9f31bd40..5a40205d8e5 100644 --- a/src/main/java/forge/model/FModel.java +++ b/src/main/java/forge/model/FModel.java @@ -27,6 +27,7 @@ import java.util.List; import forge.Constant; import forge.Constant.Preferences; +import forge.FThreads; import forge.card.BoosterData; import forge.card.CardBlock; import forge.card.CardRulesReader; @@ -43,7 +44,6 @@ import forge.game.MatchController; import forge.game.limited.GauntletMini; import forge.game.player.LobbyPlayer; import forge.gauntlet.GauntletData; -import forge.gui.GuiUtils; import forge.item.CardDb; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; @@ -161,7 +161,7 @@ public enum FModel { this.loadDynamicGamedata(); // Loads all cards (using progress bar). - GuiUtils.checkEDT("CardFactory$constructor", false); + FThreads.checkEDT("CardFactory$constructor", false); final CardStorageReader reader = new CardStorageReader(NewConstants.CARD_DATA_DIR, true); try { // this fills in our map of card names to Card instances.