source: filezilla/trunk/fuentes/src/engine/event_loop.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 4.6 KB
Line 
1#include <filezilla.h>
2
3#include "event_loop.h"
4
5#include <algorithm>
6
7CEventLoop::CEventLoop()
8        : wxThread(wxTHREAD_JOINABLE)
9        , sync_(false)
10{
11        Create();
12        Run();
13}
14
15CEventLoop::~CEventLoop()
16{
17        {
18                scoped_lock lock(sync_);
19                quit_ = true;
20                cond_.signal(lock);
21        }
22
23        Wait(wxTHREAD_WAIT_BLOCK);
24
25        scoped_lock lock(sync_);
26        for (auto & v : pending_events_) {
27                delete v.second;
28        }
29}
30
31void CEventLoop::SendEvent(CEventHandler* handler, CEventBase* evt)
32{
33        {
34                scoped_lock lock(sync_);
35                if (!handler->removing_) {
36                        if (pending_events_.empty()) {
37                                cond_.signal(lock);
38                        }
39                        pending_events_.emplace_back(handler, evt);                     
40                        return;
41                }
42        }
43
44        delete evt;
45}
46
47void CEventLoop::RemoveHandler(CEventHandler* handler)
48{
49        scoped_lock l(sync_);
50
51        handler->removing_ = true;
52
53        pending_events_.erase(
54                std::remove_if(pending_events_.begin(), pending_events_.end(),
55                        [&](Events::value_type const& v) {
56                                if (v.first == handler) {
57                                        delete v.second;
58                                }
59                                return v.first == handler;
60                        }
61                ),
62                pending_events_.end()
63        );
64
65        timers_.erase(
66                std::remove_if(timers_.begin(), timers_.end(),
67                        [&](timer_data const& v) {
68                                return v.handler_ == handler;
69                        }
70                ),
71                timers_.end()
72        );
73        if (timers_.empty()) {
74                deadline_ = CMonotonicClock();
75        }
76
77        while (active_handler_ == handler) {
78                l.unlock();
79                wxMilliSleep(1);
80                l.lock();
81        }
82}
83
84void CEventLoop::FilterEvents(std::function<bool(Events::value_type &)> const& filter)
85{
86        scoped_lock l(sync_);
87
88        pending_events_.erase(
89                std::remove_if(pending_events_.begin(), pending_events_.end(),
90                        [&](Events::value_type & v) {
91                                bool const remove = filter(v);
92                                if (remove) {
93                                        delete v.second;
94                                }
95                                return remove;
96                        }
97                ),
98                pending_events_.end()
99        );
100}
101
102timer_id CEventLoop::AddTimer(CEventHandler* handler, duration const& interval, bool one_shot)
103{
104        timer_data d;
105        d.handler_ = handler;
106        if (!one_shot) {
107                d.interval_ = interval;
108        }
109        d.deadline_ = CMonotonicClock::now() + interval;
110
111        scoped_lock lock(sync_);
112        if (!handler->removing_) {
113                d.id_ = ++next_timer_id_; // 64bit, can this really ever overflow?
114
115                timers_.emplace_back(d);
116                if (!deadline_ || d.deadline_ < deadline_) {
117                        // Our new time is the next timer to trigger
118                        deadline_ = d.deadline_;
119                        cond_.signal(lock);
120                }
121        }
122        return d.id_;
123}
124
125void CEventLoop::StopTimer(timer_id id)
126{
127        if (id) {
128                scoped_lock lock(sync_);
129                for (auto it = timers_.begin(); it != timers_.end(); ++it) {
130                        if (it->id_ == id) {
131                                timers_.erase(it);
132                                if (timers_.empty()) {
133                                        deadline_ = CMonotonicClock();
134                                }
135                                break;
136                        }
137                }
138        }
139}
140
141bool CEventLoop::ProcessEvent(scoped_lock & l)
142{
143        Events::value_type ev{};
144
145        if (pending_events_.empty()) {
146                return false;
147        }
148        ev = pending_events_.front();
149        pending_events_.pop_front();
150
151        wxASSERT(ev.first);
152        wxASSERT(ev.second);
153        wxASSERT(!ev.first->removing_);
154
155        active_handler_ = ev.first;
156
157        l.unlock();
158        (*ev.first)(*ev.second);
159        delete ev.second;
160        l.lock();
161
162        active_handler_ = 0;
163       
164        return true;
165}
166
167wxThread::ExitCode CEventLoop::Entry()
168{
169        scoped_lock l(sync_);
170        while (!quit_) {
171                CMonotonicClock const now(CMonotonicClock::now());
172                if (ProcessTimers(l, now)) {
173                        continue;
174                }
175                if (ProcessEvent(l)) {
176                        continue;
177                }
178
179                // Nothing to do, now we wait
180                if (deadline_) {
181                        int wait = static_cast<int>((deadline_ - now).get_milliseconds());
182                        cond_.wait(l, wait);
183                }
184                else {
185                        cond_.wait(l);
186                }
187        }
188
189        return 0;
190}
191
192bool CEventLoop::ProcessTimers(scoped_lock & l, CMonotonicClock const& now)
193{
194        if (!deadline_ || now < deadline_) {
195                // There's no deadline or deadline has not yet expired
196                return false;
197        }
198
199        // Update deadline_, stop at first expired timer
200        deadline_ = CMonotonicClock();
201        auto it = timers_.begin();
202        for (; it != timers_.end(); ++it) {
203                if (!deadline_ || it->deadline_ < deadline_) {
204                        if (it->deadline_ <= now) {
205                                break;
206                        }
207                        deadline_ = it->deadline_;
208                }
209        }
210
211        if (it != timers_.end()) {
212                // 'it' is now expired
213                // deadline_ has been updated with prior timers
214                // go through remaining elements to update deadline_
215                for (auto it2 = std::next(it); it2 != timers_.end(); ++it2) {
216                        if (!deadline_ || it2->deadline_ < deadline_) {
217                                deadline_ = it2->deadline_;
218                        }
219                }
220
221                CEventHandler *const handler = it->handler_;
222                auto const id = it->id_;
223                       
224                // Update the expired timer
225                if (!it->interval_) {
226                        timers_.erase(it);
227                }
228                else {
229                        it->deadline_ = std::move(now + it->interval_);
230                        if (!deadline_ || it->deadline_ < deadline_) {
231                                deadline_ = it->deadline_;
232                        }
233                }
234
235                // Call event handler
236                wxASSERT(!handler->removing_);
237               
238                active_handler_ = handler;
239               
240                l.unlock();
241                (*handler)(CTimerEvent(id));
242                l.lock();
243               
244                active_handler_ = 0;
245
246                return true;
247        }
248
249        return false;
250}
Note: See TracBrowser for help on using the repository browser.