Topics

Multithreading question


danielb987
 

I have a method ConditionalNG.execute() that may be called from any thread. This method calls the method object.execute() on the GUI thread. The method object.execute() calls other methods that in turn may call the method ConditionalNG.execute().

I don't want the method object.execute() to be called recursively so I must protect from that. But if ConditionalNG.execute() is called while it's running, it must remember that so that it calls object.execute() again once that method is finished. The method ConditionalNG.execute() may be called multiple times before object.execute() is finished and if so, it must call object.execute() only once.

I'm thinking of something like the code below, but need to get it to work in a multithreading environment.


class ConditionalNG {

boolean isExecuting = false;
boolean executeAgain = false;

public void execute() {
if (isExecuting) {
executeAgain = true;
} else {
do {
isExecuting = true;
executeAgain = false;

ThreadingUtil.runOnGUI(() -> {
object.execute();
}

isExecuting = false;

} while (executeAgain);
executeAgain = false;
}
}

}

Any suggestions on how this method should look like?

ConditionalNG is similar to Conditional in Logix, but I have no control over the method object.execute().

Daniel


danielb987
 

I think I have solved the problem.



class ConditionalNG {

private final ExecuteLock executeLock = new ExecuteLock();

public void execute() {
if (executeLock.get()) {
while (executeLock.loop()) {
ThreadingUtil.runOnGUI(() -> {
object.execute();
});
}
}
}

}





/**
* Protect the ConditionalNG.execute() method.
* That method may be called recursively from different threads.
*/
public class ExecuteLock {

private boolean lock = false;
private boolean again = false;

/**
* Get the status of the lock.
* If the call succeeds, the caller is responsible to loop while the method
* loop() returns true.
* @return true if the caller gets the lock.
*/
public boolean get() {
synchronized(this) {
again = true;
if (! lock) {
lock = true;
return true;
} else {
return false;
}
}
}

/**
* Get the status of the lock during loop.
* The caller is responsible to loop while the method returns true.
* @return true if the caller still has the lock.
*/
public boolean loop() {
synchronized(this) {
if (again) {
again = false;
return true;
} else {
lock = false;
return false;
}
}
}

}





2019-10-04 01:47 skrev danielb987:

I have a method ConditionalNG.execute() that may be called from any
thread. This method calls the method object.execute() on the GUI
thread. The method object.execute() calls other methods that in turn
may call the method ConditionalNG.execute().
I don't want the method object.execute() to be called recursively so I
must protect from that. But if ConditionalNG.execute() is called while
it's running, it must remember that so that it calls object.execute()
again once that method is finished. The method ConditionalNG.execute()
may be called multiple times before object.execute() is finished and
if so, it must call object.execute() only once.
I'm thinking of something like the code below, but need to get it to
work in a multithreading environment.
class ConditionalNG {
boolean isExecuting = false;
boolean executeAgain = false;
public void execute() {
if (isExecuting) {
executeAgain = true;
} else {
do {
isExecuting = true;
executeAgain = false;
ThreadingUtil.runOnGUI(() -> {
object.execute();
}
isExecuting = false;
} while (executeAgain);
executeAgain = false;
}
}
}
Any suggestions on how this method should look like?
ConditionalNG is similar to Conditional in Logix, but I have no
control over the method object.execute().
Daniel


Svata Dedic
 

Dne 04. 10. 19 v 8:22 danielb987 napsal(a):
                ThreadingUtil.runOnGUI(() -> {
                    object.execute();
If it is absolutely necessary for the executed code to (potentially) block the GUI thread... (sorry don't know the intended use, perhaps UI feedback is provided)

public class ExecuteLock {
    private boolean lock = false;
    private boolean again = false;
[...]
1/ In a situation, when object.execute() recursively execute()s AND an external thread ALSO execute()s (tries to) before current execution completes, object.execute() will be run just once more. Is that according to your requirements ?

2/ Rigidly, the object job's execution is determined at the monitor exit in loop(). If by a chance a contending thread attempts to execute() in between monitor exit in loop() and actual object.execute() start within GUI thread, an additional object.execute() will run. OK ?

3/ If the thread, that (initializes ConditionNG ?) assigns `object' could be different from *first* thread that enters execute(), either declare `object' final, or have at least one ExecuteLock's monitor exit on the thread that assigns `object' before any execution can reach execute().

-S.

Nitpick: if the guard will be exposed as API, consider renaming get() to something like once(), single(). In the API case, I'd consider to implement j.u.concurrent.Executor, if possible.


Bob Jacobsen
 

I’m really not sure what you mean by ' calls the method object.execute() on the GUI thread’, but the usual way to do this is to use standard methods to run `object.execute()` _later_ on the thread. You queue is as a later action on the GUI thread:

https://www.jmri.org/JavaDoc/doc/jmri/util/ThreadingUtil.html#runOnGUIEventually-jmri.util.ThreadingUtil.ThreadAction-

"Run some GUI-specific code at some later point.
If invoked from the GUI thread, the work is guaranteed to happen only after the current routine has returned."

By doing it later, it serializes anything that might otherwise be a recursion.

Bob

On Oct 3, 2019, at 4:47 PM, danielb987 <db123@...> wrote:

I have a method ConditionalNG.execute() that may be called from any thread. This method calls the method object.execute() on the GUI thread. The method object.execute() calls other methods that in turn may call the method ConditionalNG.execute().
--
Bob Jacobsen
@BobJacobsen