4
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.SWT;


public class GUITest3 {


    //Declare sections of GUI
    private static Shell shell = new Shell();
    CLabel sensorTitleLabel = new CLabel(shell, SWT.SHADOW_OUT);
    CLabel sensorNum_1 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    CLabel lblBattery = new CLabel(shell, SWT.SHADOW_OUT);
    CLabel lblLastAlert = new CLabel(shell, SWT.SHADOW_OUT);
    CLabel lblAlert = new CLabel(shell, SWT.SHADOW_OUT);
    CLabel batter_1 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    final CLabel lstAlert_1 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    CLabel alert_1 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    CLabel sensorNum_2 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    CLabel battery_2 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    CLabel lstAlert_2 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);
    CLabel alert_2 = new CLabel(shell, SWT.BORDER | SWT.SHADOW_IN);


    public GUITest3()
    {
        //Default constructor sets up the values for the GUI
        shell.setSize(450, 300);
        shell.setText("SWT Application");

        sensorTitleLabel.setAlignment(SWT.CENTER);
        sensorTitleLabel.setBounds(10, 10, 100, 21);
        sensorTitleLabel.setText("Sensor Number");

        sensorNum_1.setBounds(10, 37, 100, 21);
        sensorNum_1.setText("");

        lblBattery.setAlignment(SWT.CENTER);
        lblBattery.setText("Battery");
        lblBattery.setBounds(116, 10, 100, 21);

        lblLastAlert.setAlignment(SWT.CENTER);
        lblLastAlert.setText("Last Alert");
        lblLastAlert.setBounds(222, 10, 100, 21);

        lblAlert.setAlignment(SWT.CENTER);
        lblAlert.setText("Alert");
        lblAlert.setBounds(328, 10, 100, 21);

        batter_1.setText("");
        batter_1.setBounds(116, 37, 100, 21);

        lstAlert_1.setText("");
        lstAlert_1.setBounds(222, 37, 100, 21);

        alert_1.setText("");
        alert_1.setBounds(328, 37, 100, 21);

        sensorNum_2.setText("");
        sensorNum_2.setBounds(10, 64, 100, 21);

        battery_2.setText("");
        battery_2.setBounds(116, 64, 100, 21);

        lstAlert_2.setText("");
        lstAlert_2.setBounds(222, 64, 100, 21);

        alert_2.setText("");
        alert_2.setBounds(328, 64, 100, 21);
        synchronized(this)
        {
            //Starts the timer, passing in the GUI
            new AlertTimer(this).start();
        }
    }
    /**
     * Launch the application.
     * @param args
     */
    public static void main(String[] args) {

        try {
            GUITest3 window = new GUITest3();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Open the window.
     */
    public void open() {
        Display display = Display.getDefault();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    public void setTimerLabel(final String timeLeft)
    {
        //For changing the displayed time
        Display.getDefault().asyncExec(new Runnable() {
            public void run()
            {
                lstAlert_1.setText(timeLeft);
            }
        });
    }




}

class AlertTimer {
    private static final int TIMER_PERIOD = 1000;   //Sounts in one second intervals
    protected static final int MAX_COUNT = 300;     //Max count, a.k.a. five minutes
    private GUITest3 gui;
    private int count = 0;

    public AlertTimer(GUITest3 screenie) {
        gui = screenie;
        String text = (count / 60)+":0"+(count % 60);
        screenie.setTimerLabel(text);
    }

    public void start() {
        new Timer(TIMER_PERIOD, new ActionListener() {
            @Override
            public synchronized void actionPerformed(ActionEvent e) {
                if (count < MAX_COUNT) {
                    count++;
                    String text = "";
                    if((count % 60) > 9)
                    {
                        text = (count / 60)+":"+(count % 60);
                    }
                    else
                        text = (count / 60)+":0"+(count % 60);
                    System.out.println(text);
                    gui.setTimerLabel(text); 
                } else {
                    //((Timer) e.getSource()).stop();
                    Display.getDefault().syncExec(new Runnable() {
                        public void run()
                        {
                            synchronized (gui)
                            {
                                AlertFlash alrt1 = new AlertFlash(gui);
                                Thread flashing = new Thread(alrt1);
                                flashing.start();
                            }
                        }
                    });
                }
            }
        }).start();
    }

}

So, this is a gui that displays a timer and begins counting up, stopping when it hits 5:00 (does some stuff then, but still working on that, I know there are problems with it, but I'm going to keep pottering with that while I'm thinking about this timer). For some reason I can't figure out, the timer will occasionally just stop. I've had the timer stop counting at 4:18, 0:02, 2:13 etc, I spent some time seeing if there was a pattern to it, to see if that would lead me to any conclusions but I can't see a pattern. No error message either, it just stops.

I'm thinking it's something to do with the Thread aspect (where I admit, my knowledge is quite lacking, still getting there), maybe a missing 'Synchronized' or 'volatile' or similar, I've tried adding in and removing the odd 'synchronized'/'volatile' where I thought it made sense, but still same problem.

Have also tried putting the AlertTimer code in a separate class, implementing Runnable and running it through a different Thread, but same thing, the timer stops ticking at random points.

Kinda running out of ideas on what to try, or where I'm going wrong. Hope you guys can help! Thanks for reading.

Rundown
  • 43
  • 3
  • 1
    So I tried testing this in a number of different ways. First I thought it might be deadlock, but I really don't think so. Then I thought it might be GC, but I'm like 99.9% sure that isn't the case. I would recommend trying to use a different timer class all together and see if you have similar results. For what its worth, I found through my testing that this particular timer doesn't function well, and becomes pretty unreliable when the machine is under load. I would also recommend that you remove your synchronized blocks from your code, they don't appear to be required in this context. – Mark W Nov 22 '13 at 19:40

3 Answers3

2

I haven't used SWT, but I guess in this case it's very similar to Swing or JavaFX.

You should never do this in an update queue. It's meant to be a minor non-blocking operation, otherwise it could just block further updates, and this is what's happenning in your case:

Display.getDefault().syncExec(new Runnable() { // <- try changing it to asyncExec
    public void run()
    {
        synchronized (gui)                     // <-- try removing this
        {
            AlertFlash alrt1 = new AlertFlash(gui);
            Thread flashing = new Thread(alrt1);
            flashing.start();
        }
    }
});

One other possible cause could be syncExec(runnable) which blocks action execution - this should also not happen inside an action. You could try using asyncExec(runnable) to avoid this.

Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • Found a workaround by re-writing the whole class from scratch, but your answer seems to be most likely to be what was wrong. I did originally have `asyncExec(runnable)`, changed it while I was mucking around to try and fix the problem. – Rundown Dec 09 '13 at 11:26
0

The syncExec suspends execution while the task is run and the timer is a single thread so if syncExec takes more than TIMER_PERIOD ms to complete next iteration will be delayed.

aalku
  • 2,860
  • 2
  • 23
  • 44
0

I had exactly the same problem with javax.swing.Timer. As specified by the author in one comment, you should rewrite the code using java.util.Timer and java.util.TimerTask.

  • Convert the ActionListener to TimerTask
  • Convert the timer.start to timer.scheduleAtFixedRate(timerTask, 1000, 1000); (to count each second after one second)
  • Convert the timer.stop to timerTask.cancel

I end up with something like this :

class MyTimerTask extends TimerTask {
    @Override
    public void run() {
        count++;
    }
}

MyTimerTask timerTask = null;
int count = 0;

public void start() {
    count = 0;
    timerTask = new MyTimerTask();
    timer.scheduleAtFixedRate(timerTask, 1000, 1000);
}

public void stop() {
    if (timerTask != null)
        timerTask.cancel();
}
TrapII
  • 2,219
  • 1
  • 15
  • 15