]> git.tdb.fi Git - libs/core.git/blob - source/io/poll.cpp
Fix a memory leak in Poller
[libs/core.git] / source / io / poll.cpp
1 #include <cerrno>
2 #include <stdexcept>
3 #ifndef WIN32
4 #include <poll.h>
5 #endif
6 #include <msp/core/systemerror.h>
7 #include <msp/strings/format.h>
8 #include <msp/time/units.h>
9 #include "eventobject.h"
10 #include "handle.h"
11 #include "handle_private.h"
12 #include "poll.h"
13
14 using namespace std;
15
16 namespace {
17
18 using namespace Msp;
19 using namespace Msp::IO;
20
21 inline short int sys_poll_event(PollEvent event)
22 {
23         int result = 0;
24
25         if(event&~(P_INPUT|P_PRIO|P_OUTPUT))
26                 throw invalid_argument("sys_poll_event");
27
28 #ifndef WIN32
29         if(event&P_INPUT)
30                 result |= POLLIN;
31         if(event&P_PRIO)
32                 result |= POLLPRI;
33         if(event&P_OUTPUT)
34                 result |= POLLOUT;
35 #endif
36
37         return result;
38 }
39
40 inline PollEvent poll_event_from_sys(int event)
41 {
42         PollEvent result = P_NONE;
43
44 #ifdef WIN32
45         (void)event;
46 #else
47         if(event&POLLIN)
48                 result = result|P_INPUT;
49         if(event&POLLPRI)
50                 result = result|P_PRIO;
51         if(event&POLLOUT)
52                 result = result|P_OUTPUT;
53         if(event&POLLERR)
54                 result = result|P_ERROR;
55 #endif
56
57         return result;
58 }
59
60 inline PollEvent do_poll(EventObject &obj, PollEvent pe, int timeout)
61 {
62 #ifdef WIN32
63         if(timeout<0)
64                 timeout = INFINITE;
65
66         DWORD ret = WaitForSingleObject(*obj.get_event_handle(), timeout);
67         if(ret==WAIT_OBJECT_0)
68                 return pe;
69         else if(ret==WAIT_FAILED)
70                 throw system_error("WaitForSingleObject");
71
72         return P_NONE;
73 #else
74         pollfd pfd = { *obj.get_event_handle(), sys_poll_event(pe), 0 };
75
76         int ret = ::poll(&pfd, 1, timeout);
77         if(ret==-1)
78         {
79                 if(errno==EINTR)
80                         return P_NONE;
81                 else
82                         throw system_error("poll");
83         }
84
85         return poll_event_from_sys(pfd.revents);
86 #endif
87 }
88
89 }
90
91
92 namespace Msp {
93 namespace IO {
94
95 struct Poller::Private
96 {
97 #ifdef WIN32
98         vector<HANDLE> handles;
99 #else
100         vector<pollfd> pfd;
101 #endif
102 };
103
104
105 Poller::Poller():
106         priv(new Private),
107         objs_changed(false)
108 { }
109
110 Poller::~Poller()
111 {
112         delete priv;
113 }
114
115 void Poller::set_object(EventObject &obj, PollEvent ev)
116 {
117         // Verify that the object has an event handle
118         if(ev)
119                 obj.get_event_handle();
120
121         EventMap::iterator i = objects.find(&obj);
122         if(i!=objects.end())
123         {
124                 if(ev)
125                         i->second = ev;
126                 else
127                         objects.erase(i);
128
129                 objs_changed = true;
130         }
131         else if(ev)
132         {
133 #ifdef WIN32
134                 if(objects.size()>=MAXIMUM_WAIT_OBJECTS)
135                         throw logic_error("Maximum number of wait objects reached");
136 #endif
137                 objects.insert(EventMap::value_type(&obj, ev));
138
139                 objs_changed = true;
140         }
141 }
142
143 int Poller::poll()
144 {
145         return do_poll(-1);
146 }
147
148 int Poller::poll(const Time::TimeDelta &timeout)
149 {
150         if(timeout<Time::zero)
151                 throw invalid_argument("Poller::poll");
152
153         return do_poll(static_cast<int>(timeout/Time::msec));
154 }
155
156 void Poller::rebuild_array()
157 {
158 #ifdef WIN32
159         priv->handles.clear();
160
161         for(EventMap::iterator i=objects.begin(); i!=objects.end(); ++i)
162                 priv->handles.push_back(*i->first->get_event_handle());
163 #else
164         priv->pfd.clear();
165
166         for(EventMap::iterator i=objects.begin(); i!=objects.end(); ++i)
167         {
168                 pollfd p;
169                 p.fd = *i->first->get_event_handle();
170                 p.events = sys_poll_event(i->second);
171                 priv->pfd.push_back(p);
172         }
173 #endif
174
175         objs_changed = false;
176 }
177
178 int Poller::do_poll(int timeout)
179 {
180         if(objs_changed)
181                 rebuild_array();
182
183         poll_result.clear();
184
185 #ifdef WIN32
186         if(timeout<0)
187                 timeout = INFINITE;
188
189         DWORD ret = WaitForMultipleObjects(priv->handles.size(), &priv->handles.front(), false, timeout);
190         if(/*ret>=WAIT_OBJECT_0 &&*/ ret<WAIT_OBJECT_0+priv->handles.size())
191         {
192                 EventMap::iterator i = objects.begin();
193                 advance(i, ret-WAIT_OBJECT_0);
194                 poll_result.push_back(Slot(i->first, i->second));
195
196                 return 1;
197         }
198         else if(ret==WAIT_FAILED)
199                 throw system_error("WaitForMultipleObjects");
200
201         return 0;
202 #else
203         int ret = ::poll(&priv->pfd.front(), priv->pfd.size(), timeout);
204         if(ret==-1)
205         {
206                 if(errno==EINTR)
207                         return 0;
208                 else
209                         throw system_error("poll");
210         }
211
212         int n = ret;
213         EventMap::iterator j = objects.begin();
214         for(vector<pollfd>::iterator i=priv->pfd.begin(); (i!=priv->pfd.end() && n>0); ++i, ++j)
215                 if(i->revents)
216                 {
217                         poll_result.push_back(Slot(j->first, poll_event_from_sys(i->revents)));
218                         --n;
219                 }
220
221         return ret;
222 #endif
223 }
224
225
226 PollEvent poll(EventObject &obj, PollEvent pe)
227 {
228         return do_poll(obj, pe, -1);
229 }
230
231 PollEvent poll(EventObject &obj, PollEvent pe, const Time::TimeDelta &timeout)
232 {
233         if(timeout<Time::zero)
234                 throw invalid_argument("poll");
235
236         return do_poll(obj, pe, static_cast<int>(timeout/Time::msec));
237 }
238
239 } // namespace IO
240 } // namespace Msp