Home » Modeling » EMF » [Transaction] Trigger timeout while waiting for transaction
| |
Re: [Transaction] Trigger timeout while waiting for transaction [message #1249845 is a reply to message #1248741] |
Tue, 18 February 2014 17:48 |
Felix Dorner Messages: 295 Registered: March 2012 |
Senior Member |
|
|
Hi,
I feel I have two alternatives:
a) Before submitting the command to the command stack,
check if there's currently a transaction active. If so, wait for a
second and try again. Some sort of polling. But there will always be
a race condition: The user dialog might open (and start the transaction)
right between I poll and submit the command. So I don't like that one.
b) Submit the command through a second thread and "cancel" it if I see
that it hasn't started running after some time. I haven't found any
other solution than something like the code below, which still feels
kind of wrong to me.. I have played around with interrupting the thread
on which I submit the command, but that doesn't do anything (I don't
fully understand the internal Locking implementation of EMFT, but I
think it just clears interrupt status without timing out the request to
aquire the transaction lock).
Thanks again for comments, I'm kind of lost here..
Felix
//
// Some network thread calls this to run something in my editing domain.
// The runnable should be called in a transaction unless there's
// another command already blocking the domain.
//
public void test(final Runnable r) throws InterruptedException,
ExecutionException {
final AtomicBoolean running = new AtomicBoolean();
final AtomicBoolean abort = new AtomicBoolean();
final TransactionalEditingDomain d = getEditingDomain();
Callable<?> c = new Callable<Object>(){
@Override
public Object call() {
d.getCommandStack().execute(new RecordingCommand(d){
@Override
public void doExecute(){
running.set(true);
if (abort.get()){
throw new OperationCanceledException();
}
r.run();
}
});
return null;
}
};
Future<?> f = Executors.newSingleThreadExecutor().submit(c);
try {
f.get(1, TimeUnit.SECONDS);
} catch (TimeoutException exception_p) {
// after a second, callable hasn't completed yet.
if (!running.get()){
// doExecute wasn't called yet, some other transaction must be
blocking us.
abort.set(true); // flag the command to not run its runnable
any more.. (Still might run if doExecute was called in the meantime)
} else {
// doExecute was already called, so the command is executing.
wait for it
// to finish.
f.get();
}
}
}
|
|
|
Re: [Transaction] Trigger timeout while waiting for transaction [message #1250703 is a reply to message #1249845] |
Wed, 19 February 2014 14:03 |
|
Hi, Felix,
Sorry, I thought your earlier follow-up had resolved the problem.
See some replies in-line, below.
HTH,
Christian
On 2014-02-18 17:48:47 +0000, Felix Dorner said:
> Hi,
>
> I feel I have two alternatives:
>
> a) Before submitting the command to the command stack,
> check if there's currently a transaction active. If so, wait for a
> second and try again. Some sort of polling. But there will always be
> a race condition: The user dialog might open (and start the
> transaction) right between I poll and submit the command. So I don't
> like that one.
And so you shouldn't. That is not a nice race condition.
> b) Submit the command through a second thread and "cancel" it if I see
> that it hasn't started running after some time. I haven't found any
> other solution than something like the code below, which still feels
> kind of wrong to me.. I have played around with interrupting the thread
> on which I submit the command, but that doesn't do anything (I don't
> fully understand the internal Locking implementation of EMFT, but I
> think it just clears interrupt status without timing out the request to
> aquire the transaction lock).
Hmm. If the thread interrupt bubbles up to the Lock::acquire(...)
method in EMFT, then it really should break out with an
InterruptedException. Otherwise, this is a bug in EMFT. Not that this
helps you.
I think that EMFT needs to add a new transaction option for time-out.
But, again, that is no help for now. It would be a useful tool in any
case.
What your application is trying to do is to present two modal dialogs
at the same time (not actually user-friendly, interrupting a workflow)
and both of these dialogs are trying to run transactions on separate
modal-context threads (neither of which is the UI thread). Because the
second dialog is modal, the first dialog is inaccessible and that
dialog's transaction lock can never be released.
The problem is that the the second dialog is modal. Change that to a
non-modal dialog, and the user will be able to complete the first
dialog (I think). In any case, I'm not sure that it's appropriate for
the second dialog to be modal if it is popped up by some component not
in response to a user action. If it is popped up in response to what
the user is doing in the first dialog, then it should probably
piggy-back on that dialog's transaction, most simply by operating in
the same thread as the first dialog.
Another tool that may help you is the "privileged runnable." See the
Javadoc of the
TransactionalEditingDomain::createPrivilegedRunnable(Runnable) API.
The privileged runnable can be executed on any thread and, for the
duration, "borrows" the transaction currently active in the editing
domain from whatever thread owns it. This is intended specifically for
complicated UI interactions like yours, where it is known that the
thread that owns the transaction is serving some UI that is currently
blocked or otherwise idle, so it is safe for another thread to take
over for a bit.
So, perhaps your second dialog can first check whether the domain has
an active transaction and create a privileged runnable, which is
expected to be able to run safely because the second dialog knows that
it is modal.
>
> Thanks again for comments, I'm kind of lost here..
> Felix
>
> //
> // Some network thread calls this to run something in my editing domain.
> // The runnable should be called in a transaction unless there's
> // another command already blocking the domain.
> //
> public void test(final Runnable r) throws InterruptedException,
> ExecutionException {
>
> final AtomicBoolean running = new AtomicBoolean();
> final AtomicBoolean abort = new AtomicBoolean();
>
> final TransactionalEditingDomain d = getEditingDomain();
> Callable<?> c = new Callable<Object>(){
> @Override
> public Object call() {
> d.getCommandStack().execute(new RecordingCommand(d){
> @Override
> public void doExecute(){
> running.set(true);
> if (abort.get()){
> throw new OperationCanceledException();
> }
> r.run();
> }
> });
> return null;
> }
> };
>
> Future<?> f = Executors.newSingleThreadExecutor().submit(c);
> try {
> f.get(1, TimeUnit.SECONDS);
> } catch (TimeoutException exception_p) {
>
> // after a second, callable hasn't completed yet.
>
> if (!running.get()){
> // doExecute wasn't called yet, some other transaction must be
> blocking us.
> abort.set(true); // flag the command to not run its runnable
> any more.. (Still might run if doExecute was called in the meantime)
> } else {
>
> // doExecute was already called, so the command is executing.
> wait for it
> // to finish.
> f.get();
> }
> }
>
>
> }
|
|
|
Re: [Transaction] Trigger timeout while waiting for transaction [message #1250870 is a reply to message #1250703] |
Wed, 19 February 2014 17:34 |
Felix Dorner Messages: 295 Registered: March 2012 |
Senior Member |
|
|
On 19/02/2014 15:03, Christian W. Damus wrote:
> Sorry, I thought your earlier follow-up had resolved the problem.
Hehe, Yes I was waiting for you in particular :) Thanks for revisiting!
> Hmm. If the thread interrupt bubbles up to the Lock::acquire(...)
> method in EMFT, then it really should break out with an
> InterruptedException. Otherwise, this is a bug in EMFT. Not that this
> helps you.
It would kind of help, I could just blame someone else :D.
I will check again, but this requires a new day and a load of caffeine.
> What your application is trying to do is to present two modal dialogs at
> the same time (not actually user-friendly, interrupting a workflow) and
> both of these dialogs are trying to run transactions on separate
> modal-context threads (neither of which is the UI thread).
The first one actually is running in the UI thread, it's started like
this using a Recording command:
doExecute(){
if (SomeWizardDialog.open() == CANCEL){ // this blocks
// rollback changes made in the dialog
throw new OperationCanceledException();
}
}
The whole idea behind this is that the wizard allows edition of multiple
properties, and a single undo will undo all editions. I would implement
this differently but well... I can't change it now.
> The problem is that the the second dialog is modal. Change that to a
> non-modal dialog, and the user will be able to complete the first dialog
> (I think).
I changed it to run as a job, using some similar approach as the code I
sent in the previous message.
> In any case, I'm not sure that it's appropriate for the
> second dialog to be modal if it is popped up by some component not in
> response to a user action.
It is popped in response to the same physical user but from a different
application (The two apps are RCP and talk via ECF).
Thanks again for your reply, I will tell you if I find something in EMFT.
Felix.
|
|
|
Re: [Transaction] Trigger timeout while waiting for transaction [message #1250916 is a reply to message #1250870] |
Wed, 19 February 2014 18:30 |
|
On 2014-02-19 17:34:16 +0000, Felix Dorner said:
-------- 8< --------
>> What your application is trying to do is to present two modal dialogs at
>> the same time (not actually user-friendly, interrupting a workflow) and
>> both of these dialogs are trying to run transactions on separate
>> modal-context threads (neither of which is the UI thread).
>
> The first one actually is running in the UI thread, it's started like
> this using a Recording command:
>
> doExecute(){
> if (SomeWizardDialog.open() == CANCEL){ // this blocks
> // rollback changes made in the dialog
> throw new OperationCanceledException();
> }
> }
Ah, I see. If both were on the UI thread, you'd be OK. The problem
isn't so much that both aren't on the UI thread as that not both are on
the UI thread.
> The whole idea behind this is that the wizard allows edition of
> multiple properties, and a single undo will undo all editions. I would
> implement this differently but well... I can't change it now.
I just pushed today a new enhancement in Papyrus (bug 402525 [1]) that
implements single undoable operations for dialogs on the undo stack, a
problem perhaps very similar to yours. The idea is to create and edit
any number of objects in a dialog (and cascading series of sub-dialogs
launched from them) as a single transaction. Previously, these dialogs
in Papyrus executed an operation for each discrete edit (as in the
Properties view); the Cancel button wasn't enabled because all edits
took effect immediately, and to undo the edits done in a dialog would
require multiple Cmd+Zs. The solution ended up being a specialized
command stack that supported nested command execution, resulting in
nested transactions and wrapping the initial invocation of the modal
dialog in an undoable operation (the root transaction).
>> The problem is that the the second dialog is modal. Change that to a
>> non-modal dialog, and the user will be able to complete the first dialog
>> (I think).
>
> I changed it to run as a job, using some similar approach as the code I
> sent in the previous message.
That sounds reasonable to me.
>> In any case, I'm not sure that it's appropriate for the
>> second dialog to be modal if it is popped up by some component not in
>> response to a user action.
>
> It is popped in response to the same physical user but from a different
> application (The two apps are RCP and talk via ECF).
Hmm, interesting. Definitely sounds like a case for the privileged
runnable. But that's hard to impose (as an integrator) on applications
that might normally assume they're operating in isolation.
> Thanks again for your reply, I will tell you if I find something in EMFT.
>
> Felix.
[1] https://bugs.eclipse.org/bugs/show_bug.cgi?id=402525
|
|
|
Goto Forum:
Current Time: Fri Sep 20 20:45:26 GMT 2024
Powered by FUDForum. Page generated in 0.02890 seconds
|