From: Ian Jackson Date: Sat, 2 Apr 2011 17:20:06 +0000 (+0100) Subject: thread fixes: coroutine-style explicit multithread management, compiles X-Git-Tag: 1.0.5~10 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=jarrg-ian.git;a=commitdiff_plain;h=0221b0110ea743074baa49d49a92c7a6cf65ea0a thread fixes: coroutine-style explicit multithread management, compiles --- diff --git a/src/net/chiark/yarrg/MarketUploader.java b/src/net/chiark/yarrg/MarketUploader.java index 5dc7e6c..dda1538 100644 --- a/src/net/chiark/yarrg/MarketUploader.java +++ b/src/net/chiark/yarrg/MarketUploader.java @@ -43,6 +43,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { private JLabel arbitrageResult = null; private int unknownPCTBcommods = 0; private long startTime = 0; + private ProgressMonitor progmon = null; private final static String PCTB_LIVE_HOST_URL = "http://pctb.crabdance.com/"; private final static String PCTB_TEST_HOST_URL = "http://pctb.ilk.org/"; @@ -71,26 +72,6 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { private HashMap commodMap; 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(); - } - } - }; - /* * UTILITY METHODS AND SUBCLASSES * @@ -265,14 +246,14 @@ 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(); + private void progressNote(final String s_in) throws Exception { + new UIX() { public void body() { + String arb = null; + arb = arbitrageResult.getText(); + String s = s_in; if (arb != null && arb.length() != 0) s = "" + arb + "
" + s; - pm.setNote(s); + progmon.setNote(s); }}.exec(); } @@ -360,7 +341,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * a Window named "Puzzle Pirates" though. */ private void createGUI() { - // on event thread + on_ui_thread(); frame = new JFrame("Jarrg Uploader"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); GridLayout layout = new GridLayout(2,1); @@ -370,6 +351,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { findMarket = new JButton("Upload Market Data"); findMarket.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + on_ui_thread(); findMarket.setEnabled(false); resultSummary.setText(""); arbitrageResult.setText(""); @@ -383,12 +365,21 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { error(e.toString()); e.printStackTrace(); } - EventQueue.invokeAndWait(new Runnable() { public void run() { - if(sidePanel != null) { - sidePanel.removePropertyChangeListener(changeListener); - } - findMarket.setEnabled(true); - }}); + try { + new UIX() { public void body() { + if(sidePanel != null) { + sidePanel.removePropertyChangeListener(changeListener); + } + if (progmon != null) { + progmon.close(); + progmon = null; + } + findMarket.setEnabled(true); + }}.exec(); + } catch (Exception e) { + System.err.println("exception tidying on UI thread:"); + e.printStackTrace(); + } } }.start(); } @@ -415,28 +406,41 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * 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() { + private abstract class UIXR implements Runnable { + public abstract ReturnType bodyr(); + public ReturnType return_value; + public void run() { return_value = bodyr(); }; + public ReturnType exec() throws Exception { if (EventQueue.isDispatchThread()) { - r.run(); + this.run(); } else { - invokeAndWait(r); + EventQueue.invokeAndWait(this); } + return return_value; }; }; + private abstract class UIX extends UIXR implements Runnable { + public abstract void body(); + public Object bodyr() { body(); return null; } + }; - private void error(String msg) { - new UI() { public booolean body() { - resultSummary.setText("failed"); - JOptionPane.showMessageDialog(frame,msg,"Error", - JOptionPane.ERROR_MESSAGE); - return true; - }}.exec(); + private void on_ui_thread() { assert(EventQueue.isDispatchThread()); } + private void on_our_thread() { assert(!EventQueue.isDispatchThread()); } + + private void error(final String msg) { + try { + new UIX() { public void body() { + resultSummary.setText("failed"); + JOptionPane.showMessageDialog(frame,msg,"Error", + JOptionPane.ERROR_MESSAGE); + }}.exec(); + } catch (Exception e) { + System.err.println("exception reporting to UI thread:"); + e.printStackTrace(); + } } - private void error_html(String msg, String html) { + private void error_html(final String msg, String html) { Pattern body = Pattern.compile("(.*)", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); Matcher m = body.matcher(html); @@ -454,6 +458,17 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { error(whole_msg); } + + private void setProgress(final int nv) throws Exception { + new UIX() { public void body() { + progmon.setProgress(nv); + }}.exec(); + } + private boolean isCanceled() throws Exception { + return new UIXR() { public Boolean bodyr() { + return new Boolean(progmon.isCanceled()); + }}.exec().booleanValue(); + } /* * GUI MANIPULATION CALLBACKS @@ -461,6 +476,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { private PropertyChangeListener changeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { + on_ui_thread(); if(e.getNewValue() != null && e.getPropertyName().equals (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { @@ -479,7 +495,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { }; private void getIsland() { - // runs on event thread + on_ui_thread(); // If the league tracker is there, we can skip the faff // and ask for its tooltip, since we're on a boat @@ -531,7 +547,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * oceanName variable */ private void getOcean() { - // runs on event thread + on_ui_thread(); oceanName = null; AccessibleContext topwindow = window.getAccessibleContext(); oceanName = topwindow.getAccessibleName() @@ -561,6 +577,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { }; private void runUpload() throws Exception { + on_our_thread(); + boolean doneyarrg = false, donepctb = false; YarrgTimestampFetcher yarrgts_thread = null; @@ -572,26 +590,26 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { yarrgts_thread.start(); } - AccessibleTable accesstable = null; - new UI() { public boolean body() { - ProgressMonitor pm = new ProgressMonitor + final AccessibleTable accesstable = + new UIXR() { public AccessibleTable bodyr() { + progmon = new ProgressMonitor (frame,"Processing Market Data","Getting table data",0,100); - pm.setMillisToDecideToPopup(0); - pm.setMillisToPopup(0); + progmon.setMillisToDecideToPopup(0); + progmon.setMillisToPopup(0); - accesstable = findMarketTable(); - if(accesstable == null) { + AccessibleTable at = findMarketTable(); + if(at == null) { error("Market table not found!"+ " Please open the Buy/Sell Commodities interface."); - return; + return null; } - if(accesstable.getAccessibleRowCount() == 0) { + if(at.getAccessibleRowCount() == 0) { error("No data found, please wait for the table to have data first!"); - return; + return null; } if(!isDisplayAll()) { error("Please select \"All\" from the Display: popup menu."); - return; + return null; } progresslog("(async) getisland..."); @@ -599,6 +617,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { progresslog("getocean..."); getOcean(); progresslog("getocean done"); + + return at; }}.exec(); if (accesstable == null) return; @@ -622,22 +642,23 @@ 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" }; + final ArrayList> data = + new UIXR>> + () { public ArrayList> bodyr() { + String headings_expected[] = new String[] + { "Commodity", "Trading outlet", "Buy price", + "Will buy", "Sell price", "Will sell" }; - new UI() { public void body() { ArrayList> headers = getData(accesstable.getAccessibleColumnHeader()); if (headers.size() != 1) { error("Table headings not one row! " + headers.toString()); - return; + return null; } if (headers.get(0).size() < 6 || headers.get(0).size() > 7) { error("Table headings not six or seven columns! " + headers.toString()); - return; + return null; } for (int col=0; col> data = getData(accesstable); + return getData(accesstable); }}.exec(); - if (!data) return; + if (data == null) return; if (showArbitrage) { progresslog("arbitrage..."); @@ -664,8 +685,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { if (uploadToYarrg && yarrgts != null) { progresslog("yarrg prepare..."); - progressNote(pm, "Yarrg: Preparing data"); - pm.setProgress(10); + progressNote("Yarrg: Preparing data"); + setProgress(10); StringBuilder yarrgsb = new StringBuilder(); String yarrgdata; // string containing what we'll feed to yarrg @@ -684,7 +705,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { yarrgdata = yarrgsb.toString(); - progressNote(pm, "Yarrg: Uploading"); + progressNote("Yarrg: Uploading"); progresslog("yarrg upload..."); doneyarrg = runYarrg(yarrgts, oceanName, islandName, yarrgdata); @@ -693,17 +714,17 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { if (uploadToPCTB) { progresslog("pctb prepare..."); - progressNote(pm, "PCTB: Getting stall names"); - pm.setProgress(20); - if(pm.isCanceled()) { + progressNote("PCTB: Getting stall names"); + setProgress(20); + if(isCanceled()) { return; } TreeSet buys = new TreeSet(); TreeSet sells = new TreeSet(); LinkedHashMap stallMap = getStallMap(data); - pm.setProgress(40); - progressNote(pm, "PCTB: Sorting offers"); - if(pm.isCanceled()) { + setProgress(40); + progressNote("PCTB: Sorting offers"); + if(isCanceled()) { return; } // get commod map @@ -719,9 +740,9 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { // if (dtxt!=null) dtxt.println("\n\n\n"+buys); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - pm.setProgress(60); - progressNote(pm, "PCTB: Sending data"); - if(pm.isCanceled()) { + setProgress(60); + progressNote("PCTB: Sending data"); + if(isCanceled()) { return; } GZIPOutputStream out = new GZIPOutputStream(outStream); @@ -739,27 +760,33 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { InputStream in = sendInitialData(new ByteArrayInputStream(ba)); progresslog("pctb sent."); if (in == null) return; - pm.setProgress(80); - if(pm.isCanceled()) { + setProgress(80); + if(isCanceled()) { return; } - progressNote(pm, "PCTB: Waiting ..."); + progressNote("PCTB: Waiting ..."); progresslog("pctb finish..."); donepctb = finishUpload(in); progresslog("pctb done."); } - pm.setProgress(100); + setProgress(99); + String summary; if ((uploadToPCTB && !donepctb) || (uploadToYarrg && !doneyarrg)) { - resultSummary.setText("trouble"); + summary= "trouble"; } else if (unknownPCTBcommods != 0) { - resultSummary.setText("PCTB lacks "+unknownPCTBcommods+" commod"); + summary= "PCTB lacks "+unknownPCTBcommods+" commod(s)"; } else if (donepctb || doneyarrg) { - resultSummary.setText("Done " + islandName); + summary= "Done " + islandName; } else { - resultSummary.setText("uploaded nowhere!"); + summary= "uploaded nowhere!"; } + final String summary_final = summary; + new UIX() { public void body() { + resultSummary.setText(summary_final); + }}.exec(); + progresslog("done."); } @@ -771,6 +798,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * @return an array of record arrays, each representing a row of the table */ private ArrayList> getData(AccessibleTable table) { + on_ui_thread(); ArrayList> data = new ArrayList>(); for (int i = 0; i < table.getAccessibleRowCount(); i++) { ArrayList row = new ArrayList(); @@ -788,6 +816,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * otherwise null */ public AccessibleTable findMarketTable() { + on_ui_thread(); Accessible node1 = window; Accessible node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,0,1,0,0}); // commod market @@ -815,6 +844,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * invalid. */ private Accessible descendNodes(Accessible parent, int[] path) { + on_ui_thread(); for(int i=0;inull if the child is not found. */ private Accessible descend(Accessible parent, int childNum) { + on_ui_thread(); if (parent == null) return null; int children = parent.getAccessibleContext().getAccessibleChildrenCount(); if (childNum >= children) { @@ -853,6 +884,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * @return the child or null if the child is not found. */ private Accessible descendByClass(Accessible parent, String classname) { + on_ui_thread(); if (parent == null) return null; AccessibleContext ac = parent.getAccessibleContext(); int children = ac.getAccessibleChildrenCount(); @@ -879,6 +911,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * otherwise false */ private boolean isDisplayAll() { + on_ui_thread(); Accessible button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,0,0,1}); if(!(button instanceof JButton)) { button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,1,0,0,0,1}); @@ -897,6 +930,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * the commodity id. */ private HashMap getCommodMap() { + on_our_thread(); if(commodMap != null) { return commodMap; } @@ -1144,6 +1178,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * @param file an InputStream open to the gzipped data we want to send */ private InputStream sendInitialData(InputStream file) throws IOException { + on_our_thread(); ClientHttpRequest http = new ClientHttpRequest(PCTB_HOST_URL + "upload.php"); http.setParameter("marketdata","marketdata.gz",file,"application/gzip"); @@ -1177,6 +1212,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { * @param in stream of data from the server to read */ private boolean finishUpload(InputStream in) throws IOException { + on_our_thread(); + String html = readstreamstring(in); debug_write_stringdata("pctb-initial.html", html); Matcher m; @@ -1238,6 +1275,7 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { private InputStream post_for_yarrg(ClientHttpRequest http) throws IOException { + on_our_thread(); if (!http.post()) { String err = readstreamstring(http.resultstream()); error("

Error reported by YARRG server

\n" + err); @@ -1333,7 +1371,8 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { } private @SuppressWarnings("unchecked") - void calculateArbitrage(ArrayList> data) { + void calculateArbitrage(ArrayList> data) + throws InterruptedException { int arbitrage = 0; ArrayList> arb_bs = null; String lastcommod = null; @@ -1364,12 +1403,16 @@ implements Runnable, TopLevelWindowListener, GUIInitializedListener { } } arbitrage += calculateArbitrageCommodity(arb_bs); + String arb; if (arbitrage != 0) { - arbitrageResult.setText("arbitrage: "+arbitrage - +" poe"); + arb = "arbitrage: "+arbitrage+" poe"; } else { - arbitrageResult.setText("no arbitrage"); + arb = "no arbitrage"; } + final String arb_final = arb; + EventQueue.invokeLater(new Runnable() { public void run() { + arbitrageResult.setText(arb_final); + }}); } }