]> git.tdb.fi Git - libs/core.git/blob - source/poll.cpp
Allow polling an empty set of objects
[libs/core.git] / source / poll.cpp
1 /* $Id$
2
3 This file is part of libmspio
4 Copyright © 2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7 #include <errno.h>
8 #include <msp/strings/formatter.h>
9 #include <msp/time/units.h>
10 #include "except.h"
11 #include "base.h"
12 #include "poll.h"
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 InvalidParameterValue("Invalid poll events");
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         // Stop the compiler from complaining about unused parameter
44         event=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(Base &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 SystemError("Poll failed", GetLastError());
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 SystemError("Poll failed", errno);
82         }
83
84         return poll_event_from_sys(pfd.revents);
85 #endif
86 }
87
88 }
89
90 namespace Msp {
91 namespace IO {
92
93 Poller::Poller():
94         pfd_dirty(false)
95 { }
96
97 void Poller::set_object(Base &obj, PollEvent ev)
98 {
99         // Verify that the object has an event handle
100         if(ev)
101                 obj.get_event_handle();
102
103         SlotMap::iterator i=objects.find(&obj);
104         if(i!=objects.end())
105         {
106                 if(ev)
107                         i->second.events=ev;
108                 else
109                         objects.erase(i);
110
111                 pfd_dirty=true;
112         }
113         else if(ev)
114         {
115 #ifdef WIN32
116                 if(objects.size()>=MAXIMUM_WAIT_OBJECTS)
117                         throw InvalidState("Maximum number of wait objects reached");
118 #endif
119                 objects.insert(SlotMap::value_type(&obj, Slot(&obj, ev)));
120
121                 pfd_dirty=true;
122         }
123 }
124
125 int Poller::poll()
126 {
127         return do_poll(-1);
128 }
129
130 int Poller::poll(const Time::TimeDelta &timeout)
131 {
132         if(timeout<Time::zero)
133                 throw InvalidParameterValue("Invalid timeout");
134
135         return do_poll(static_cast<int>(timeout/Time::msec));
136 }
137
138 void Poller::rebuild_pfd()
139 {
140         pfd.clear();
141
142         pollfd p;
143
144         for(SlotMap::iterator i=objects.begin(); i!=objects.end(); ++i)
145         {
146                 p.fd=i->second.object->get_event_handle();
147 #ifndef WIN32
148                 p.events=sys_poll_event(i->second.events);
149 #endif
150                 pfd.push_back(p);
151         }
152
153         pfd_dirty=false;
154 }
155
156 int Poller::do_poll(int timeout)
157 {
158         if(pfd_dirty)
159                 rebuild_pfd();
160
161         poll_result.clear();
162
163 #ifdef WIN32
164         if(timeout<0)
165                 timeout=INFINITE;
166
167         DWORD ret=WaitForMultipleObjects(pfd.size(), &pfd.front().fd, false, timeout);
168         if(/*ret>=WAIT_OBJECT_0 &&*/ ret<WAIT_OBJECT_0+pfd.size())
169         {
170                 SlotMap::iterator i=objects.begin();
171                 advance(i, ret-WAIT_OBJECT_0);
172                 poll_result.push_back(Slot(i->second.object, i->second.events));
173
174                 return 1;
175         }
176         else if(ret==WAIT_FAILED)
177                 throw SystemError("Poll failed", GetLastError());
178
179         return 0;
180 #else
181         int ret=::poll(&pfd.front(), pfd.size(), timeout);
182         if(ret==-1)
183         {
184                 if(errno==EINTR)
185                         return 0;
186                 else
187                         throw SystemError("Poll failed", errno);
188         }
189
190         int n=ret;
191         SlotMap::iterator j=objects.begin();
192         for(std::vector<pollfd>::iterator i=pfd.begin(); (i!=pfd.end() && n>0); ++i,++j)
193                 if(i->revents)
194                 {
195                         poll_result.push_back(Slot(j->second.object, poll_event_from_sys(i->revents)));
196                         --n;
197                 }
198
199         return ret;
200 #endif
201 }
202
203
204 PollEvent poll(Base &obj, PollEvent pe)
205 {
206         return do_poll(obj, pe, -1);
207 }
208
209 PollEvent poll(Base &obj, PollEvent pe, const Time::TimeDelta &timeout)
210 {
211         if(timeout<Time::zero)
212                 throw InvalidParameterValue("Invalid timeout");
213
214         return do_poll(obj, pe, static_cast<int>(timeout/Time::msec));
215 }
216
217 } // namespace IO
218 } // namespace Msp