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