Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #11740 > unrolled thread

Lightweight postDelayed / removeCallbacks

Started byJan Burse <janburse@fastmail.fm>
First post2012-02-04 20:44 +0100
Last post2012-02-06 01:35 +0100
Articles 4 — 2 participants

Back to article view | Back to comp.lang.java.programmer


Contents

  Lightweight postDelayed / removeCallbacks Jan Burse <janburse@fastmail.fm> - 2012-02-04 20:44 +0100
    Re: Lightweight postDelayed / removeCallbacks Steven Simpson <ss@domain.invalid> - 2012-02-05 22:00 +0000
      Re: Lightweight postDelayed / removeCallbacks Jan Burse <janburse@fastmail.fm> - 2012-02-06 01:15 +0100
        Re: Lightweight postDelayed / removeCallbacks Jan Burse <janburse@fastmail.fm> - 2012-02-06 01:35 +0100

#11740 — Lightweight postDelayed / removeCallbacks

FromJan Burse <janburse@fastmail.fm>
Date2012-02-04 20:44 +0100
SubjectLightweight postDelayed / removeCallbacks
Message-ID<jgk1q9$9o1$1@news.albasani.net>
Dear All,

Just fidling around with a Android / Swing port.
Just wonder whether somebody has worked on an
untility to provide the following under Swing
which is inspired by the Android View(*):

    postDelayed(Runnable r, int d):
         Posts an event on the EDT, which will
         invoked r after a delay of d millisecond.

    removeCallbacks(Runnable r):
         Immediately remove all events from the
         EDT that would invoke r.

One could use javax.swing.Timer for the first
method. Something along:

     public void postDelayed(final Runnable r, int d) {
         final Timer t=new Timer(d,new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                r.run();
             }
         });
         t.setRepeats(false);
         t.start();
     }

But then for the second method one would need to
track the created timers, so as to be able to
selectively stop them.

Anybody already implemented a corresponding utility
in the spirit of SwingUtil.invokeLater/invokeAndWait?
Would be glad to receive hint so as not to reinvent
the wheel.

Bye

(*)
http://developer.android.com/reference/android/view/View.html

[toc] | [next] | [standalone]


#11750

FromSteven Simpson <ss@domain.invalid>
Date2012-02-05 22:00 +0000
Message-ID<ipa309-7pn.ln1@news.simpsonst.f2s.com>
In reply to#11740
On 04/02/12 19:44, Jan Burse wrote:
>    postDelayed(Runnable r, int d):
>         Posts an event on the EDT, which will
>         invoked r after a delay of d millisecond.
>
>    removeCallbacks(Runnable r):
>         Immediately remove all events from the
>         EDT that would invoke r.
>
> One could use javax.swing.Timer for the first
> method. Something along:
>
>     public void postDelayed(final Runnable r, int d) {
>         final Timer t=new Timer(d,new ActionListener() {
>             public void actionPerformed(ActionEvent e) {
>                r.run();
>             }
>         });
>         t.setRepeats(false);
>         t.start();
>     }
>
> But then for the second method one would need to
> track the created timers, so as to be able to
> selectively stop them.

A first stab:

Keep a WeakHashMap<Runnable,Collection<Reference<Timer>>>, making all 
changes on the EDT.  Store weak references to each created Timer, in a 
HashSet per Runnable.

When asked to remove all callbacks, remove the Runnable from the map, 
get its Collection of Timers, and stop them.  If the references have 
already expired, you don't care.  When all the Timers using the same 
Runnable have expired, assuming that they naturally decay, the map entry 
will be removed anyway.

   // uncompiled
   void removeCallbacks(final Runnable r) {
     invokeAndWait(new Runnable() {
       public void run() {
         Collection<Reference<Timer>>  timers = map.remove(r);
         if (timers == null) return;
         for (Reference<Timer>  rt : timers) {
           Timer t = rt.get();
           if (t != null) t.stop();
         }
       }
     });
   }

   void postDelayed(final Runnable r, int d) {
     final Timer t=new Timer(d,new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         r.run();
       }
     });
     invokeAndWait(new Runnable() {
       public void run() {
         Collection<Reference<Timer>>  coll = map.get(r);
         if (coll == null) {
           coll = new HashSet<Reference<Timer>>();
           map.put(r, coll);
         }
         coll.add(new WeakReference<Timer>(t));
       }
     });
     // should use d too!
     t.setRepeats(false);
     t.start();
   }




-- 
ss at comp dot lancs dot ac dot uk

[toc] | [prev] | [next] | [standalone]


#11751

FromJan Burse <janburse@fastmail.fm>
Date2012-02-06 01:15 +0100
Message-ID<jgn62j$4de$1@news.albasani.net>
In reply to#11750
Steven Simpson schrieb:
 > A first stab:

Thank you very much for your solution. Since I
was in need of a solution I implemented already
something yesterday.

Difference to your stab: postDelayed() and
removeCallbacks() do not wait for mouse /
keyevents / etc.. to flush. The problem
is that invokeAndWait() calls postEvent(),
which calls flushPendingEvents() in turn.

The Android removeCallbacks() and postDelayed()
are also immediate. They call in turn
removeMessages() respectively enqueueMessage()
which are immediate.

I didn't use javax.swing.Timer since I still
think they are not leightweight enough. So
my waiting is done via Object.wait(), whereas
the Swing timer uses Condition.awaitNanos().
Not sure what is more reliable.

I also use System.currentTimeMillis() whereas
the Swing timer uses System.nanoTime(). nanoTime()
is more stable, it does not change when the
wall clock is changed on the computer.

But current solution is then to use tail
recursion for a looping animation. Which
can get jerky, since the rate will be not
fixed. The delivery of the event might still
take some time before a next postDelayed()
happens, since it is delivered via postEvent()
which uses flushPendingEvents(). But this is
tollerated in the current application.

Could use a post with runnable = null to stop
the thread, something similar is done in the
Android looper. But currently the thread has to
be started via start(), and can then be easily
terminated via interrupt().

---------------- Begin Code ------------------

public class ThreadTimer extends Thread {
     private final ArrayList<TimerEntry> entrylist =
        new ArrayList<TimerEntry>();
     private final Object entrylock = new Object();

     public void postDelayed(Runnable r, int d) {
         TimerEntry entry = new TimerEntry(r,
             System.currentTimeMillis() + d);
         synchronized (entrylock) {
             for (int i = 0; i < entrylist.size(); i++) {
                 TimerEntry entry2 = entrylist.get(i);
                 if (entry.getWhen() < entry2.getWhen()) {
                     entrylist.add(i, entry);
                     entrylock.notifyAll();
                     return;
                 }
             }
             entrylist.add(entry);
             entrylock.notifyAll();
         }
     }

     public void removeCallbacks(Runnable r) {
         synchronized (entrylock) {
             int backsize = entrylist.size();
             for (int i = entrylist.size() - 1; i >= 0; i--) {
                 TimerEntry entry = entrylist.get(i);
                 if (entry.getRunnable() == r)
                     entrylist.remove(i);
             }
             if (backsize != entrylist.size())
                 entrylock.notifyAll();
         }
     }

     private TimerEntry take() throws InterruptedException {
         for (; ; ) {
             synchronized (entrylock) {
                 if (entrylist.size() == 0) {
                     entrylock.wait();
                 } else {
                     TimerEntry entry = entrylist.get(0);
                     long now = System.currentTimeMillis();
                     if (entry.getWhen() <= now) {
                         entrylist.remove(0);
                         return entry;
                     } else {
                         entrylock.wait(entry.getWhen() - now);
                     }
                 }
             }
         }
     }

     public void run() {
         try {
             for (; ; ) {
                 TimerEntry entry = take();
                 SwingUtilities.invokeLater(entry.getRunnable());
             }
         } catch (InterruptedException x) {
             /* */
         }
     }

}

class TimerEntry {
     private final Runnable runnable;
     private final long when;

     TimerEntry(Runnable r, long w) {
         runnable = r;
         when = w;
     }

     Runnable getRunnable() {
         return runnable;
     }

     long getWhen() {
         return when;
     }

}

---------------- End Code --------------------


[toc] | [prev] | [next] | [standalone]


#11752

FromJan Burse <janburse@fastmail.fm>
Date2012-02-06 01:35 +0100
Message-ID<jgn789$6d6$1@news.albasani.net>
In reply to#11751
Jan Burse schrieb:
> ---------------- Begin Code ------------------

Correction:

     private TimerEntry take() throws InterruptedException {
         synchronized (entrylock) {
             for (; ; ) {

Since wait() and wait(int) anyway kind of
release the lock. See also the DelayQueue
implementation in java.util.concurrent.

Bye

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.programmer


csiph-web