Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » [Transaction] Trigger timeout while waiting for transaction
[Transaction] Trigger timeout while waiting for transaction [message #1248741] Mon, 17 February 2014 16:07 Go to next message
Felix Dorner is currently offline Felix DornerFriend
Messages: 295
Registered: March 2012
Senior Member
Hi,

The application I work with has a modal dialog that starts a transaction
when the dialog opens, and commits it when it closes.

Another component may ask to write to the ResourceSet while that dialog
is open. The current behaviour is that the modal dialog is covered by
another dialog (Somehow created by EMF Transaction) covers the modal
dialog. The new dialog has a cancel button, but pressing it just greys
out the dialog. This results in a deadlock. I can neither close the
dialog for the waiting transaction, nor the modal dialog that was opened
by the user.

Any ideas on how I could fix that? The best would be if:

- The user presses cancel on the second dialog and this will react
immediately and cancel the second transaction request

- If the user doesn't press cancel, timeout the operation automatically
after a few seconds, so that the thread that tried to create the second
transaction can find out that the timeout was triggered.

Felix
Re: [Transaction] Trigger timeout while waiting for transaction [message #1248749 is a reply to message #1248741] Mon, 17 February 2014 16:17 Go to previous messageGo to next message
Felix Dorner is currently offline Felix DornerFriend
Messages: 295
Registered: March 2012
Senior Member
On 17/02/2014 17:07, Felix Dorner wrote:
> Hi,
>
> The application I work with has a modal dialog that starts a transaction
> when the dialog opens, and commits it when it closes.
>
> Another component may ask to write to the ResourceSet while that dialog
> is open. The current behaviour is that the modal dialog is covered by
> another dialog (Somehow created by EMF Transaction)

Uhm, no. Somehow created by me.. ^^:

new
ProgressMonitorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()).run(true,
true, runnable_p);

where the runnable_p is the code that queues the command and blocks. So
it's at least clear that pressing the cancel button here won't do anything.
Re: [Transaction] Trigger timeout while waiting for transaction [message #1249845 is a reply to message #1248741] Tue, 18 February 2014 17:48 Go to previous messageGo to next message
Felix Dorner is currently offline Felix DornerFriend
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 Go to previous messageGo to next message
Christian W. Damus is currently offline Christian W. DamusFriend
Messages: 1199
Registered: July 2009
Location: Canada
Senior Member

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 Go to previous messageGo to next message
Felix Dorner is currently offline Felix DornerFriend
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 Go to previous message
Christian W. Damus is currently offline Christian W. DamusFriend
Messages: 1199
Registered: July 2009
Location: Canada
Senior Member

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
Previous Topic:getting all instances of an Ecore class in the workspace
Next Topic:[CDO] Could not activate TCPClientConnector on CDO Hibernate Client/server example
Goto Forum:
  


Current Time: Thu Dec 12 16:35:21 GMT 2019

Powered by FUDForum. Page generated in 0.01937 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top