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