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