Chronograph.java revision 77f2b82a2e80af8da52c22d69a76def6d4209757
1package com.android.server.wifi.hotspot2;
2
3import java.util.ArrayList;
4import java.util.HashSet;
5import java.util.Iterator;
6import java.util.List;
7import java.util.Map;
8import java.util.Set;
9import java.util.TreeMap;
10
11public class Chronograph extends Thread {
12    private final Map<Long, Set<AlarmEntry>> mAlarmEntryMap = new TreeMap<Long, Set<AlarmEntry>>();
13    private boolean mRecalculate;
14
15    private static class AlarmEntry {
16        private final long mAt;
17        private final AlarmHandler mAlarmHandler;
18        private final Object mToken;
19
20        private AlarmEntry(long at, AlarmHandler alarmHandler, Object token) {
21            mAt = at;
22            mAlarmHandler = alarmHandler;
23            mToken = token;
24        }
25
26        private void callout() {
27            mAlarmHandler.wake(mToken);
28        }
29    }
30
31    public Chronograph()
32    {
33        setName("Chronograph");
34        setDaemon(true);
35    }
36
37    public Object addAlarm(long interval, AlarmHandler handler, Object token) {
38        long at = System.currentTimeMillis() + interval;
39        synchronized (mAlarmEntryMap) {
40            AlarmEntry alarmEntry = new AlarmEntry(at, handler, token);
41            Set<AlarmEntry> entries = mAlarmEntryMap.get(at);
42            if (entries == null) {
43                entries = new HashSet<AlarmEntry>(1);
44                mAlarmEntryMap.put(at, entries);
45            }
46            entries.add(alarmEntry);
47            mRecalculate = true;
48            mAlarmEntryMap.notifyAll();
49            return alarmEntry;
50        }
51    }
52
53    public boolean cancelAlarm(Object key) {
54        if (key == null || key.getClass() != AlarmEntry.class) {
55            throw new IllegalArgumentException("Not an alarm key");
56        }
57
58        AlarmEntry alarmEntry = (AlarmEntry)key;
59
60        synchronized (mAlarmEntryMap) {
61            Set<AlarmEntry> entries = mAlarmEntryMap.get(alarmEntry.mAt);
62            if (entries == null) {
63                return false;
64            }
65            if (entries.remove(alarmEntry)) {
66                mRecalculate = true;
67                mAlarmEntryMap.notifyAll();
68                return true;
69            }
70            return false;
71        }
72    }
73
74    @Override
75    public void run() {
76
77        for(;;) {
78
79            long now = System.currentTimeMillis();
80            List<Set<AlarmEntry>> pending = new ArrayList<Set<AlarmEntry>>();
81
82            long nextExpiration = 0;
83
84            synchronized (mAlarmEntryMap) {
85
86                Iterator<Map.Entry<Long,Set<AlarmEntry>>> entries =
87                        mAlarmEntryMap.entrySet().iterator();
88
89                while (entries.hasNext()) {
90                    Map.Entry<Long,Set<AlarmEntry>> entry = entries.next();
91                    if (entry.getKey() <= now) {
92                        pending.add(entry.getValue());
93                        entries.remove();
94                    }
95                    else {
96                        nextExpiration = entry.getKey();
97                        break;
98                    }
99                }
100            }
101
102            for (Set<AlarmEntry> alarmEntries : pending) {
103                for (AlarmEntry alarmEntry : alarmEntries) {
104                    alarmEntry.callout();
105                }
106            }
107
108            now = System.currentTimeMillis();
109
110            synchronized (mAlarmEntryMap) {
111                long sleep = nextExpiration - now;
112                while (sleep > 0 && !mRecalculate) {
113                    try {
114                        mAlarmEntryMap.wait(sleep);
115                    }
116                    catch (InterruptedException ie) {
117                        /**/
118                    }
119                    sleep = nextExpiration - System.currentTimeMillis();
120                }
121            }
122        }
123    }
124
125    public static void main(String[] args) throws InterruptedException{
126        Chronograph chronograph = new Chronograph();
127        chronograph.start();
128
129        chronograph.addAlarm(3000L, new AlarmHandler() {
130            @Override
131            public void wake(Object token) {
132                System.out.println("3: " + token);
133            }
134        }, "3s" );
135
136        Object key = chronograph.addAlarm(7500L, new AlarmHandler() {
137            @Override
138            public void wake(Object token) {
139                System.out.println("7: " + token);
140            }
141        }, "7.5s" );
142
143        chronograph.addAlarm(10000L, new AlarmHandler() {
144            @Override
145            public void wake(Object token) {
146                System.out.println("10: " + token);
147            }
148        }, "10.00s" );
149
150        System.out.println(chronograph.cancelAlarm(key));
151
152        chronograph.join();
153    }
154}
155