Rename error.h to except.h
[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         //cout<<"poller set_object "<<&obj<<' '<<ev<<'\n';
104
105         SlotMap::iterator i=objects.find(&obj);
106         if(i!=objects.end())
107         {
108                 if(ev)
109                         i->second.events=ev;
110                 else
111                         objects.erase(i);
112
113                 pfd_dirty=true;
114         }
115         else if(ev)
116         {
117 #ifdef WIN32
118                 if(objects.size()>=MAXIMUM_WAIT_OBJECTS)
119                         throw InvalidState("Maximum number of wait objects reached");
120 #endif
121                 objects.insert(SlotMap::value_type(&obj, Slot(&obj, ev)));
122
123                 pfd_dirty=true;
124         }
125 }
126
127 int Poller::poll()
128 {
129         return do_poll(-1);
130 }
131
132 int Poller::poll(const Time::TimeDelta &timeout)
133 {
134         if(timeout<Time::zero)
135                 throw InvalidParameterValue("Invalid timeout");
136
137         return do_poll(static_cast<int>(timeout/Time::msec));
138 }
139
140 void Poller::rebuild_pfd()
141 {
142         pfd.clear();
143
144         pollfd p;
145
146         for(SlotMap::iterator i=objects.begin(); i!=objects.end(); ++i)
147         {
148                 p.fd=i->second.object->get_event_handle();
149 #ifndef WIN32
150                 p.events=sys_poll_event(i->second.events);
151 #endif
152                 pfd.push_back(p);
153         }
154
155         pfd_dirty=false;
156 }
157
158 int Poller::do_poll(int timeout)
159 {
160         if(pfd_dirty)
161                 rebuild_pfd();
162
163         poll_result.clear();
164
165         if(pfd.empty())
166                 throw InvalidState("Nothing to poll");
167
168 #ifdef WIN32
169         if(timeout<0)
170                 timeout=INFINITE;
171
172         DWORD ret=WaitForMultipleObjects(pfd.size(), reinterpret_cast<HANDLE *>(&pfd.front()), false, timeout);
173         if(/*ret>=WAIT_OBJECT_0 &&*/ ret<WAIT_OBJECT_0+pfd.size())
174         {
175                 SlotMap::iterator i=objects.begin();
176                 advance(i, ret-WAIT_OBJECT_0);
177                 poll_result.push_back(Slot(i->second.object, i->second.events));
178
179                 return 1;
180         }
181         else if(ret==WAIT_FAILED)
182                 throw SystemError("Poll failed", GetLastError());
183
184         return 0;
185 #else
186         int ret=::poll(&pfd.front(), pfd.size(), timeout);
187         if(ret==-1)
188         {
189                 if(errno==EINTR)
190                         return 0;
191                 else
192                         throw SystemError("Poll failed", errno);
193         }
194
195         int n=ret;
196         SlotMap::iterator j=objects.begin();
197         for(std::vector<pollfd>::iterator i=pfd.begin(); (i!=pfd.end() && n>0); ++i,++j)
198                 if(i->revents)
199                 {
200                         poll_result.push_back(Slot(j->second.object, poll_event_from_sys(i->revents)));
201                         --n;
202                 }
203
204         return ret;
205 #endif
206 }
207
208
209 PollEvent poll(Base &obj, PollEvent pe)
210 {
211         return do_poll(obj, pe, -1);
212 }
213
214 PollEvent poll(Base &obj, PollEvent pe, const Time::TimeDelta &timeout)
215 {
216         if(timeout<Time::zero)
217                 throw InvalidParameterValue("Invalid timeout");
218
219         return do_poll(obj, pe, static_cast<int>(timeout/Time::msec));
220 }
221
222 } // namespace IO
223 } // namespace Msp