0

We are developing a Simulator with Students with Java and Swing. The Problem we have is that after a Button Click in the Simulator.java the variable for private static boolean startButton change too true. After this the "if" should start doing their job, but that is not the case.

I have this Code Block in my Brain.java:


public class Brain
{
    // ==========================================================

    /*
     * This is the Part of Port A
     */
    // Tris for Port A | ARRAY VALUE 0
    private static boolean[] port_A_Tris_Checkboxes = new boolean[8];

    // PINs | ARRAY VALUE 1
    private static boolean[] port_A_Pins_Checkboxes = new boolean[5];

    /*
     * This is the Part of Port B
     */
    // Tris for Port B | ARRAY VALUE 2
    private static boolean[] port_B_Tris_Checkboxes = new boolean[8];

    // PINs | ARRAY VALUE 3
    private static boolean[] port_B_Pins_Checkboxes = new boolean[8];

    // ==========================================================

    /*
     * This is the Part of the Watchdog
     */
    private static boolean watchdogCheckbox;

    // ==========================================================

    /*
     * This is the Part of the Reset Button
     */
    private static boolean resetButton;

    // ==========================================================

    /*
     * This is the Part of the Einzelschritt Button
     */
    private static boolean einzelschrittButton;

    // ==========================================================

    /*
     * This is the Part of the Start Button
     */
    private static boolean startButton = false;

    // ==========================================================

    /*
     * This is the Part of the Stop Button
     */
    private static boolean stopButton = false;

    // ==========================================================

    public static void main(String[] args)
    {
        // startet das GUI
        Simulator.main(args);
        while (true) {

            if ((startButton == true) && (stopButton == false)) {

                // Do one step of the program
                Steuerwerk.runCommand(Decoder.decode(Simulator.getProgramMemory()
                        .getProgramMemory()[ProgramCounter.getProgramCounter()]),
                        Simulator.getProgramMemory().getProgramMemory()[ProgramCounter
                                .getProgramCounter()]);
                System.out.println(W_Register.getArgument());

            } else if (getStart() && getStop()) {

                /*
                 * If program has already started and the stop button was pressed,
                 * stop the program
                 */
                setStart(false);
                setStop(false);

            } else if (getStop()) {

                // No reason to save stopping of program if it didn't even start yet
                setStop(false);

            } else if (getEinzelschritt()) {

                // Do one step of the program
                Steuerwerk.runCommand(Decoder.decode(Simulator.getProgramMemory()
                        .getProgramMemory()[ProgramCounter.getProgramCounter()]),
                        Simulator.getProgramMemory().getProgramMemory()[ProgramCounter
                                .getProgramCounter()]);
                System.out.println(W_Register.getArgument());
                setEinzelschritt(false);
            }
        }

    }

    // ==========================================================

    /**
     * 
     * @param index : Checkbox value, true if checkbox has been marked
     * @param whichArray : Value that say which ArrayIndex is checked in which
     *        Array
     * 
     *        Diese Methode wird die in der GUI aktivierten Checkboxen hier in
     *        der Programm Logik vermerken sodass diese dann für die weitere
     *        Abarbeitung nach User wunsch geschehen und mit berüchsichtigung der
     *        aktivierten Checkboxen des Users
     */
    public static void setChechboxesForTrisOrPinBoolean(int index,
            int whichArray)
    {
        switch (whichArray) {
        case 0: {
            port_A_Tris_Checkboxes[index] = !port_A_Tris_Checkboxes[index];
            break;
        }
        case 1: {
            port_A_Pins_Checkboxes[index] = !port_A_Pins_Checkboxes[index];
            break;
        }
        case 2: {
            port_B_Tris_Checkboxes[index] = !port_B_Tris_Checkboxes[index];
            break;
        }
        case 3: {
            port_B_Pins_Checkboxes[index] = !port_B_Pins_Checkboxes[index];
            break;
        }
        default:
            throw new IllegalArgumentException("Unexpected value: " + whichArray);
        }
    }

    // ==========================================================

    /**
     * Diese methode wird die Aktivierung der Watchog Checkbox berücksichtigen
     * und eben dann auch vermerken
     */
    public static void setCheckboxForWatchdog()
    {
        watchdogCheckbox = !watchdogCheckbox;
    }

    // ==========================================================

    /*
     * The Set Method for the Reset Button
     */
    public static void setReset(boolean value)
    {
        if (value == true) {
            resetButton = true;
        } else {
            resetButton = false;
        }
    }

    /*
     * Get Method for the Reset Button Value
     */
    public static boolean getReset()
    {
        return resetButton;
    }

    // ==========================================================

    /*
     * The Set Method for the Einzelschritt Button
     */
    public static void setEinzelschritt(boolean value)
    {
        if (value == true) {
            einzelschrittButton = true;
        } else {
            einzelschrittButton = false;
        }
    }

    /*
     * Get Method for the Einzelschritt Button Value
     */
    public static boolean getEinzelschritt()
    {
        return einzelschrittButton;
    }

    // ==========================================================

    /*
     * The Set Method for the Start Button
     */
    public static void setStart(boolean value)
    {
        if (value == true) {
            startButton = true;
        } else {
            startButton = false;
        }
    }

    /*
     * Get Method for the Start Button Value
     */
    public static boolean getStart()
    {
        return startButton;
    }

    // ==========================================================

    /*
     * The Set Method for the Stop Button
     */
    public static void setStop(boolean value)
    {
        if (value == true) {
            stopButton = true;
        } else {
            stopButton = false;
        }
    }

    /*
     * Get Method for the Stop Button Value
     */
    public static boolean getStop()
    {
        return stopButton;
    }

    // ==========================================================
}

the other Part is my Simulator:

JButton btnStart = new JButton("START");
        btnStart.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e)
            {
                // TODO Auto-generated method stub
                System.out.println("Pressed");
                Brain.setStart(true);
            }
        });

The funny thing is that only if we add a System.out.println() or we Debug... it starts the if.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Leon Dikan
  • 15
  • 4
  • `Simulator.main(args);` is **not** an asynchronous call. You probably want to start another thread for that. Beware, anything that terminates `Simulator` will also terminate `Brain`. – Elliott Frisch May 28 '20 at 12:59
  • @ElliottFrisch: it seems like `Simulartor.main` starts up a Swing window and returns, so the Swing part of the app will continue running. – Joachim Sauer May 28 '20 at 13:02
  • @JoachimSauer It's difficult to say for sure without the relevant code, but I guess you must be correct. I stick to the comments unless I'm confident in my guess. I also see a `Simulator.getProgramMemory().getProgramMemory()` and that makes me question my own sanity. – Elliott Frisch May 28 '20 at 13:27
  • @ElliottFrisch That `Simulator.getProgramMemory().getProgramMemory()`is a funny thing the first Method I should call `.getProgramMemory()` get the Object the other `.getProgramMemory()` is to get the Array. I was a little bit lazy with the Names .... haha – Leon Dikan May 28 '20 at 14:18
  • @JoachimSauer Yes you guess correctly. Guys thanks! – Leon Dikan May 28 '20 at 14:22

1 Answers1

1

If you modify variables concurrently in multiple threads then they need to be volatile unless you use some other process of synchronization to ensure read barriers exist.

Most Swing code will run on the Event Dispatch Thread which is distinct from the main thread (which the non-Swing part of your code seems to be runnning on).

System.out.println will execute code that uses synchronized and as such probably works as an effective read barrier here.

Similarly using a Debugger will undo many of the optimizations that volatile also disables so it could have the same effect.

Change your private static boolean to private static volatile boolean.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • > and as such *probably^ works as an effective read barrier here That's **evil**. And yes, it does, ATM that's full memory flush. I've seen a `synchronized (new Object()) {}` monstrosity used to that effect once. – pafau k. May 28 '20 at 13:07
  • @pafauk.: I've never seen that used *intentationally* and I would suspect that's a bug, because as far as I know `synchronized` only guarantees happens-before for threads that *synchronize on the same object*. It's quite possible that in practice the effect is much wider than this, but that would be an implementation detail of the JVM and depending on it is a bad idea. – Joachim Sauer May 28 '20 at 13:11
  • My sentiments exactly. Even though I strongly believe implementation will move beyond full memory barriers, we should definitely stick to JLS with it's happens-before (and synchronizes-with) – pafau k. May 28 '20 at 13:17