From 85a3df43ff414a1c12420eddf0ee372d9682aee8 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 2 Apr 2011 16:40:28 +0100 Subject: [PATCH] thread fixes: wip coroutine-style explicit multithread management --- src/net/chiark/yarrg/MarketUploader.java | 285 ++++++++++++++--------- 1 file changed, 174 insertions(+), 111 deletions(-) diff --git a/src/net/chiark/yarrg/MarketUploader.java b/src/net/chiark/yarrg/MarketUploader.java index 4e04ee4..5dc7e6c 100644 --- a/src/net/chiark/yarrg/MarketUploader.java +++ b/src/net/chiark/yarrg/MarketUploader.java @@ -72,25 +72,31 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { public PrintStream dtxt = null; private PropertyChangeListener changeListener = new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent e) { - if(e.getNewValue() != null && - e.getPropertyName().equals - (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { - Accessible islandInfo = - descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});; - String text = - islandInfo.getAccessibleContext().getAccessibleText() - .getAtIndex(AccessibleText.SENTENCE,0); - int index = text.indexOf(":"); - String name = text.substring(0,index); - islandName = name; - // if (dtxt!=null) dtxt.println(islandName); - sidePanel.removePropertyChangeListener(this); - latch.countDown(); - } + public void propertyChange(PropertyChangeEvent e) { + if(e.getNewValue() != null && + e.getPropertyName().equals + (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { + Accessible islandInfo = + descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});; + String text = + islandInfo.getAccessibleContext().getAccessibleText() + .getAtIndex(AccessibleText.SENTENCE,0); + int index = text.indexOf(":"); + String name = text.substring(0,index); + islandName = name; + // if (dtxt!=null) dtxt.println(islandName); + sidePanel.removePropertyChangeListener(this); + latch.countDown(); } - }; + } + }; + /* + * UTILITY METHODS AND SUBCLASSES + * + * Useable on any thread. + * + */ private int parseQty(String str) { if (str.equals(">1000")) { return 1001; @@ -120,15 +126,6 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { strm.write(data); strm.close(); } - - private void progressNote(ProgressMonitor pm, String s) { - String arb = null; - if (arbitrageResult != null) - arb = arbitrageResult.getText(); - if (arb != null && arb.length() != 0) - s = "" + arb + "
" + s; - pm.setNote(s); - } /** * An abstract market offer, entailing a commodity being bought or sold by @@ -267,8 +264,24 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { } } } + + private void progressNote(ProgressMonitor pm, String s) { + String arb = null; + new UI() { public void body() { + if (arbitrageResult != null) + arb = arbitrageResult.getText(); + if (arb != null && arb.length() != 0) + s = "" + arb + "
" + s; + pm.setNote(s); + }}.exec(); + } - /** + /* + * ENTRY POINT AND STARTUP + * + * Main thread and/or event thread + */ + /* * Entry point. Read our preferences. */ public MarketUploader() { @@ -347,6 +360,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * a Window named "Puzzle Pirates" though. */ private void createGUI() { + // on event thread frame = new JFrame("Jarrg Uploader"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); GridLayout layout = new GridLayout(2,1); @@ -357,25 +371,24 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { findMarket.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { findMarket.setEnabled(false); + resultSummary.setText(""); + arbitrageResult.setText(""); new Thread() { public void run() { startTime = new Date().getTime(); - resultSummary.setText(""); - arbitrageResult.setText(""); unknownPCTBcommods = 0; try { runUpload(); } catch(Exception e) { error(e.toString()); e.printStackTrace(); - resultSummary.setText("failed"); - } finally { + } + EventQueue.invokeAndWait(new Runnable() { public void run() { if(sidePanel != null) { - // remove it if it's still attached sidePanel.removePropertyChangeListener(changeListener); } - } - findMarket.setEnabled(true); + findMarket.setEnabled(true); + }}); } }.start(); } @@ -394,11 +407,79 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { frame.pack(); } + + /* + * ERROR REPORTING AND GENERAL UTILITIES + * + * Synchronous modal dialogues + * error and error_html may be called from any thread + */ + + private Class UI implements Runnable { + public virtual void body(); + public void run() { body(); }; + public void exec() { + if (EventQueue.isDispatchThread()) { + r.run(); + } else { + invokeAndWait(r); + } + }; + }; + + private void error(String msg) { + new UI() { public booolean body() { + resultSummary.setText("failed"); + JOptionPane.showMessageDialog(frame,msg,"Error", + JOptionPane.ERROR_MESSAGE); + return true; + }}.exec(); + } - /** - * Finds the island name from the /who tab, sets global islandName variable + private void error_html(String msg, String html) { + Pattern body = Pattern.compile("(.*)", + Pattern.DOTALL | Pattern.CASE_INSENSITIVE); + Matcher m = body.matcher(html); + if (m.find()) { + html = m.group(1); + Pattern fixup = Pattern.compile("<(\\w+) */>");; + m = fixup.matcher(html); + html = m.replaceAll("<$1>"); + m = Pattern.compile("[\\r\\n]+").matcher(html); + html = m.replaceAll(" "); + } + String whole_msg = "

Error

"+msg + +"

PCTB Server said:

"+html+"
"; + if (dtxt!=null) dtxt.println("###" + whole_msg + "###"); + + error(whole_msg); + } + + /* + * GUI MANIPULATION CALLBACKS */ + + private PropertyChangeListener changeListener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + if(e.getNewValue() != null && + e.getPropertyName().equals + (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { + Accessible islandInfo = + descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});; + String text = islandInfo.getAccessibleContext().getAccessibleText() + .getAtIndex(AccessibleText.SENTENCE,0); + int index = text.indexOf(":"); + String name = text.substring(0,index); + islandName = name; + // if (dtxt!=null) dtxt.println(islandName); + sidePanel.removePropertyChangeListener(this); + latch.countDown(); + } + } + }; + private void getIsland() { + // runs on event thread // If the league tracker is there, we can skip the faff // and ask for its tooltip, since we're on a boat @@ -450,6 +531,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * oceanName variable */ private void getOcean() { + // runs on event thread oceanName = null; AccessibleContext topwindow = window.getAccessibleContext(); oceanName = topwindow.getAccessibleName() @@ -457,35 +539,6 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { } - /** - * Shows a dialog with the error msg. - * - * @param msg a String describing the error that occured. - */ - private void error(String msg) { - JOptionPane.showMessageDialog(frame,msg,"Error",JOptionPane.ERROR_MESSAGE); - } - - private void error_html(String msg, String html) { - Pattern body = Pattern.compile("(.*)", - Pattern.DOTALL | Pattern.CASE_INSENSITIVE); - Matcher m = body.matcher(html); - if (m.find()) { - html = m.group(1); - Pattern fixup = Pattern.compile("<(\\w+) */>");; - m = fixup.matcher(html); - html = m.replaceAll("<$1>"); - m = Pattern.compile("[\\r\\n]+").matcher(html); - html = m.replaceAll(" "); - } - String whole_msg = "

Error

"+msg - +"

PCTB Server said:

"+html+"
"; - if (dtxt!=null) dtxt.println("###" + whole_msg + "###"); - - JOptionPane.showMessageDialog(frame,whole_msg,"Error", - JOptionPane.ERROR_MESSAGE); - } - /** * Run the data collection process, and upload the results. This * is the method that calls most of the other worker methods for @@ -508,41 +561,46 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { }; private void runUpload() throws Exception { - progresslog("starting"); - - ProgressMonitor pm = new ProgressMonitor - (frame,"Processing Market Data","Getting table data",0,100); - pm.setMillisToDecideToPopup(0); - pm.setMillisToPopup(0); boolean doneyarrg = false, donepctb = false; YarrgTimestampFetcher yarrgts_thread = null; + progresslog("starting"); + if (uploadToYarrg) { progresslog("(async) yarrg timestamp..."); yarrgts_thread = new YarrgTimestampFetcher(); yarrgts_thread.start(); } - AccessibleTable accesstable = findMarketTable(); - if(accesstable == null) { - error("Market table not found!"+ - " Please open the Buy/Sell Commodities interface."); - return; - } - if(accesstable.getAccessibleRowCount() == 0) { - error("No data found, please wait for the table to have data first!"); - return; - } - if(!isDisplayAll()) { - error("Please select \"All\" from the Display: popup menu."); - return; - } + AccessibleTable accesstable = null; + new UI() { public boolean body() { + ProgressMonitor pm = new ProgressMonitor + (frame,"Processing Market Data","Getting table data",0,100); + pm.setMillisToDecideToPopup(0); + pm.setMillisToPopup(0); + + accesstable = findMarketTable(); + if(accesstable == null) { + error("Market table not found!"+ + " Please open the Buy/Sell Commodities interface."); + return; + } + if(accesstable.getAccessibleRowCount() == 0) { + error("No data found, please wait for the table to have data first!"); + return; + } + if(!isDisplayAll()) { + error("Please select \"All\" from the Display: popup menu."); + return; + } - progresslog("(async) getisland..."); - getIsland(); - progresslog("getocean..."); - getOcean(); - progresslog("getocean done"); + progresslog("(async) getisland..."); + getIsland(); + progresslog("getocean..."); + getOcean(); + progresslog("getocean done"); + }}.exec(); + if (accesstable == null) return; if (latch != null) { latch.await(2, java.util.concurrent.TimeUnit.SECONDS); @@ -564,34 +622,39 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { progresslog("table check..."); + ArrayList> data = null; String headings_expected[] = new String[] { "Commodity", "Trading outlet", "Buy price", "Will buy", "Sell price", "Will sell" }; - ArrayList> headers = - getData(accesstable.getAccessibleColumnHeader()); - if (headers.size() != 1) { - error("Table headings not one row! " + headers.toString()); - return; - } - if (headers.get(0).size() < 6 || - headers.get(0).size() > 7) { - error("Table headings not six or seven columns! " + headers.toString()); - return; - } - for (int col=0; col> headers = + getData(accesstable.getAccessibleColumnHeader()); + if (headers.size() != 1) { + error("Table headings not one row! " + headers.toString()); return; } - } + if (headers.get(0).size() < 6 || + headers.get(0).size() > 7) { + error("Table headings not six or seven columns! " + headers.toString()); + return; + } + for (int col=0; col> data = getData(accesstable); + ArrayList> data = getData(accesstable); + }}.exec(); + if (!data) return; if (showArbitrage) { progresslog("arbitrage..."); -- 2.30.2