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