reset one shot timer internal state instead of leaving it thinking that it's still...
[wxWidgets.git] / src / unix / timerunx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/timerunx.cpp
3 // Purpose: wxTimer implementation for non-GUI applications under Unix
4 // Author: Lukasz Michalski
5 // Created: 15/01/2005
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Lukasz Michalski
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #include "wx/wxprec.h"
20
21 #ifndef WX_PRECOMP
22 #include "wx/log.h"
23 #include "wx/module.h"
24 #include "wx/app.h"
25 #include "wx/list.h"
26 #include "wx/hashmap.h"
27 #include "wx/event.h"
28 #endif
29
30 #include "wx/longlong.h"
31
32 #include <sys/time.h>
33 #include <signal.h>
34
35 #include "wx/unix/private/timer.h"
36
37 #include "wx/listimpl.cpp"
38 WX_DEFINE_LIST(wxTimerList);
39
40 // trace mask for the debugging messages used here
41 #define wxTrace_Timer wxT("timer")
42
43 // ----------------------------------------------------------------------------
44 // local functions
45 // ----------------------------------------------------------------------------
46
47 // helper function to format wxUsecClock_t
48 static inline wxString wxUsecClockAsString(wxUsecClock_t usec)
49 {
50 #if wxUSE_LONGLONG
51 return usec.ToString();
52 #else // wxUsecClock_t == double
53 return wxString::Format(_T("%.0f"), usec);
54 #endif
55 }
56
57 // ============================================================================
58 // wxTimerScheduler implementation
59 // ============================================================================
60
61 wxTimerScheduler *wxTimerScheduler::ms_instance = NULL;
62
63 wxTimerScheduler::~wxTimerScheduler()
64 {
65 for ( wxTimerList::iterator node = m_timers.begin();
66 node != m_timers.end();
67 ++node )
68 {
69 delete *node;
70 }
71 }
72
73 void wxTimerScheduler::AddTimer(wxUnixTimerImpl *timer, wxUsecClock_t expiration)
74 {
75 DoAddTimer(new wxTimerSchedule(timer, expiration));
76 }
77
78 void wxTimerScheduler::DoAddTimer(wxTimerSchedule *s)
79 {
80 // do an insertion sort to keep the list sorted in expiration order
81 wxTimerList::iterator node;
82 for ( node = m_timers.begin(); node != m_timers.end(); ++node )
83 {
84 wxASSERT_MSG( (*node)->m_timer != s->m_timer,
85 _T("adding the same timer twice?") );
86
87 if ( (*node)->m_expiration > s->m_expiration )
88 break;
89 }
90
91 m_timers.insert(node, s);
92
93 wxLogTrace(wxTrace_Timer, wxT("Inserted timer %d expiring at %s"),
94 s->m_timer->GetId(),
95 wxUsecClockAsString(s->m_expiration).c_str());
96 }
97
98 void wxTimerScheduler::RemoveTimer(wxUnixTimerImpl *timer)
99 {
100 wxLogTrace(wxTrace_Timer, wxT("Removing timer %d"), timer->GetId());
101
102 for ( wxTimerList::iterator node = m_timers.begin();
103 node != m_timers.end();
104 ++node )
105 {
106 if ( (*node)->m_timer == timer )
107 {
108 delete *node;
109 m_timers.erase(node);
110 return;
111 }
112 }
113
114 wxFAIL_MSG( _T("removing inexistent timer?") );
115 }
116
117 bool wxTimerScheduler::GetNext(wxUsecClock_t *remaining) const
118 {
119 if ( m_timers.empty() )
120 return false;
121
122 wxCHECK_MSG( remaining, false, _T("NULL pointer") );
123
124 *remaining = (*m_timers.begin())->m_expiration - wxGetLocalTimeUsec();
125 if ( *remaining < 0 )
126 {
127 // timer already expired, don't wait at all before notifying it
128 *remaining = 0;
129 }
130
131 return true;
132 }
133
134 void wxTimerScheduler::NotifyExpired()
135 {
136 if ( m_timers.empty() )
137 return;
138
139 const wxUsecClock_t now = wxGetLocalTimeUsec();
140
141 wxTimerList::iterator cur,
142 next;
143 for ( cur = m_timers.begin(); cur != m_timers.end(); cur = next )
144 {
145 wxTimerSchedule * const s = *cur;
146 if ( s->m_expiration > now )
147 {
148 // as the list is sorted by expiration time, we can skip the rest
149 break;
150 }
151
152 // remember next as we will delete the node pointed to by cur
153 next = cur;
154 ++next;
155
156 m_timers.erase(cur);
157
158 // check whether we need to keep this timer
159 wxUnixTimerImpl * const timer = s->m_timer;
160 if ( timer->IsOneShot() )
161 {
162 // the timer needs to be stopped but don't call its Stop() from
163 // here as it would attempt to remove the timer from our list and
164 // we had already done it, so we just need to reset its state
165 timer->MarkStopped();
166
167 // don't need it any more
168 delete s;
169 }
170 else // reschedule the next timer expiration
171 {
172 s->m_expiration += timer->GetInterval()*1000;
173 DoAddTimer(s);
174 }
175
176 // and finally notify the timer
177 timer->Notify();
178 }
179 }
180
181 // ============================================================================
182 // wxUnixTimerImpl implementation
183 // ============================================================================
184
185 wxUnixTimerImpl::wxUnixTimerImpl(wxTimer* timer)
186 : wxTimerImpl(timer)
187 {
188 m_isRunning = false;
189 }
190
191 bool wxUnixTimerImpl::Start(int milliseconds, bool oneShot)
192 {
193 // notice that this will stop an already running timer
194 wxTimerImpl::Start(milliseconds, oneShot);
195
196 wxTimerScheduler::Get().AddTimer(this, wxGetLocalTimeUsec() + m_milli*1000);
197 m_isRunning = true;
198
199 return true;
200 }
201
202 void wxUnixTimerImpl::Stop()
203 {
204 if ( m_isRunning )
205 {
206 wxTimerScheduler::Get().RemoveTimer(this);
207
208 m_isRunning = false;
209 }
210 }
211
212 bool wxUnixTimerImpl::IsRunning() const
213 {
214 return m_isRunning;
215 }
216
217 wxUnixTimerImpl::~wxUnixTimerImpl()
218 {
219 wxASSERT_MSG( !m_isRunning, _T("must have been stopped before") );
220 }
221
222 // ============================================================================
223 // wxTimerUnixModule: responsible for freeing the global timer scheduler
224 // ============================================================================
225
226 class wxTimerUnixModule : public wxModule
227 {
228 public:
229 wxTimerUnixModule() {}
230 virtual bool OnInit() { return true; }
231 virtual void OnExit() { wxTimerScheduler::Shutdown(); }
232
233 DECLARE_DYNAMIC_CLASS(wxTimerUnixModule)
234 };
235
236 IMPLEMENT_DYNAMIC_CLASS(wxTimerUnixModule, wxModule)
237
238 // ============================================================================
239 // global functions
240 // ============================================================================
241
242 wxUsecClock_t wxGetLocalTimeUsec()
243 {
244 #ifdef HAVE_GETTIMEOFDAY
245 struct timeval tv;
246 if ( wxGetTimeOfDay(&tv) != -1 )
247 {
248 wxUsecClock_t val = 1000000L; // usec/sec
249 val *= tv.tv_sec;
250 return val + tv.tv_usec;
251 }
252 #endif // HAVE_GETTIMEOFDAY
253
254 return wxGetLocalTimeMillis() * 1000L;
255 }
256