Skip to content

Commit 9e26899

Browse files
wahlbrinkbuchen
andcommitted
Improve UI responsiveness when UpdatePricesJob is running on slower
computers The change ensures that running asynchronously ClientInput.setDirty in the display thread is not scheduled if the previous request has not yet been executed and thus no backlog of requests arises. Issue: portfolio-performance#5557 Co-authored-by: Stephan Wahlbrink <sw@wahlbrink.eu> Co-authored-by: Andreas Buchen <andreas.buchen@gmail.com>
1 parent 01d255a commit 9e26899

File tree

1 file changed

+63
-11
lines changed

1 file changed

+63
-11
lines changed

name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/editor/ClientInput.java

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ public class ClientInput
8383
private List<Runnable> disposeJobs = new ArrayList<>();
8484
private List<ClientInputListener> listeners = new ArrayList<>();
8585

86+
private final Object pendingLock = new Object();
87+
private boolean pendingDirty;
88+
private boolean pendingRecalculate;
89+
8690
@Inject
8791
private IEventBroker broker;
8892

@@ -148,6 +152,37 @@ public void touch()
148152
setDirty(true, false);
149153
}
150154

155+
private void scheduleDirty(boolean recalculate)
156+
{
157+
if (Display.getDefault().getThread() == Thread.currentThread())
158+
{
159+
setDirty(true, recalculate);
160+
return;
161+
}
162+
synchronized (pendingLock)
163+
{
164+
pendingRecalculate |= recalculate;
165+
if (pendingDirty)
166+
return; // asyncExec already queued; it will pick up the merged state
167+
pendingDirty = true;
168+
}
169+
Display.getDefault().asyncExec(this::applyPendingDirty);
170+
}
171+
172+
private void applyPendingDirty()
173+
{
174+
boolean recalculate;
175+
synchronized (pendingLock)
176+
{
177+
if (!pendingDirty)
178+
return; // consumed by a save() that ran before we fired
179+
recalculate = pendingRecalculate;
180+
pendingDirty = false;
181+
pendingRecalculate = false;
182+
}
183+
setDirty(true, recalculate);
184+
}
185+
151186
private void setDirty(boolean isDirty, boolean recalculate)
152187
{
153188
this.isDirty = isDirty;
@@ -230,7 +265,21 @@ public void save(Shell shell)
230265
storePreferences(false);
231266

232267
broker.post(UIConstants.Event.File.SAVED, clientFile.getAbsolutePath());
233-
setDirty(false, false);
268+
269+
// Cancel any pending asyncExec dirty notification. If a background
270+
// modification arrived during the save the on-disk file may not reflect
271+
// it, so we conservatively remain dirty in that case.
272+
boolean hadPendingModification;
273+
boolean hadPendingRecalculate;
274+
synchronized (pendingLock)
275+
{
276+
hadPendingModification = pendingDirty;
277+
hadPendingRecalculate = pendingRecalculate;
278+
pendingDirty = false;
279+
pendingRecalculate = false;
280+
}
281+
setDirty(hadPendingModification, hadPendingRecalculate);
282+
234283
listeners.forEach(ClientInputListener::onSaved);
235284
}
236285
catch (IOException e)
@@ -269,7 +318,18 @@ public void doSaveAs(Shell shell, String extension, Set<SaveFlag> flags) // NOSO
269318
storePreferences(true);
270319

271320
broker.post(UIConstants.Event.File.SAVED, clientFile.getAbsolutePath());
272-
setDirty(false, false);
321+
322+
boolean hadPendingModification;
323+
boolean hadPendingRecalculate;
324+
synchronized (pendingLock)
325+
{
326+
hadPendingModification = pendingDirty;
327+
hadPendingRecalculate = pendingRecalculate;
328+
pendingDirty = false;
329+
pendingRecalculate = false;
330+
}
331+
setDirty(hadPendingModification, hadPendingRecalculate);
332+
273333
listeners.forEach(ClientInputListener::onSaved);
274334
}
275335
catch (IOException e)
@@ -658,15 +718,7 @@ private void scheduleAutoSaveJob()
658718
// convenience: Client#markDirty can be called on any thread, but
659719
// ClientInputListener#onDirty will always be called on the UI
660720
// thread
661-
662-
if (Display.getDefault().getThread() == Thread.currentThread())
663-
{
664-
setDirty(true, recalculate);
665-
}
666-
else
667-
{
668-
Display.getDefault().asyncExec(() -> setDirty(true, recalculate));
669-
}
721+
scheduleDirty(recalculate);
670722
};
671723
client.addPropertyChangeListener(listener);
672724
disposeJobs.add(() -> client.removePropertyChangeListener(listener));

0 commit comments

Comments
 (0)