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