Handle present out of range better in sample server.
[yazpp-moved-to-github.git] / src / yaz-socket-manager.cpp
1 /*
2  * Copyright (c) 1998-2005, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: yaz-socket-manager.cpp,v 1.31 2005-01-14 10:13:50 adam Exp $
6  */
7 #ifdef WIN32
8 #include <winsock.h>
9 #endif
10
11 #if HAVE_SYS_TIME_H
12 #include <sys/time.h>
13 #endif
14 #if HAVE_SYS_TYPES_H
15 #include <sys/types.h>
16 #endif
17 #if HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20
21 #include <errno.h>
22 #include <string.h>
23 #include <assert.h>
24
25 #include <yaz/log.h>
26 #include <yaz++/socket-manager.h>
27
28 Yaz_SocketManager::YazSocketEntry **Yaz_SocketManager::lookupObserver(
29     IYazSocketObserver *observer)
30 {
31     YazSocketEntry **se;
32     
33     for (se = &m_observers; *se; se = &(*se)->next)
34         if ((*se)->observer == observer)
35             break;
36     return se;
37 }
38
39 void Yaz_SocketManager::addObserver(int fd, IYazSocketObserver *observer)
40 {
41     YazSocketEntry *se;
42
43     se = *lookupObserver(observer);
44     if (!se)
45     {
46         se = new YazSocketEntry;
47         se->next= m_observers;
48         m_observers = se;
49         se->observer = observer;
50     }
51     se->fd = fd;
52     se->mask = 0;
53     se->last_activity = 0;
54     se->timeout = -1;
55 }
56
57 void Yaz_SocketManager::deleteObserver(IYazSocketObserver *observer)
58 {
59     YazSocketEntry **se = lookupObserver(observer);
60     if (*se)
61     {
62         removeEvent (observer);
63         YazSocketEntry *se_tmp = *se;
64         *se = (*se)->next;
65         delete se_tmp;
66     }
67 }
68
69 void Yaz_SocketManager::deleteObservers()
70 {
71     YazSocketEntry *se = m_observers;
72     
73     while (se)
74     {
75         YazSocketEntry *se_next = se->next;
76         delete se;
77         se = se_next;
78     }
79     m_observers = 0;
80 }
81
82 void Yaz_SocketManager::maskObserver(IYazSocketObserver *observer, int mask)
83 {
84     YazSocketEntry *se;
85
86     yaz_log(m_log, "obs=%p read=%d write=%d except=%d", observer,
87                     mask & YAZ_SOCKET_OBSERVE_READ,
88                     mask & YAZ_SOCKET_OBSERVE_WRITE,
89                     mask & YAZ_SOCKET_OBSERVE_EXCEPT);
90
91     se = *lookupObserver(observer);
92     if (se)
93         se->mask = mask;
94 }
95
96 void Yaz_SocketManager::timeoutObserver(IYazSocketObserver *observer,
97                                         int timeout)
98 {
99     YazSocketEntry *se;
100
101     se = *lookupObserver(observer);
102     if (se)
103         se->timeout = timeout;
104 }
105
106 int Yaz_SocketManager::processEvent()
107 {
108     YazSocketEntry *p;
109     YazSocketEvent *event = getEvent();
110     int timeout = -1;
111     yaz_log (m_log, "Yaz_SocketManager::processEvent manager=%p", this);
112     if (event)
113     {
114         event->observer->socketNotify(event->event);
115         delete event;
116         return 1;
117     }
118
119     fd_set in, out, except;
120     int res;
121     int max = 0;
122     int no = 0;
123
124     FD_ZERO(&in);
125     FD_ZERO(&out);
126     FD_ZERO(&except);
127
128     time_t now = time(0);
129     for (p = m_observers; p; p = p->next)
130     {
131         int fd = p->fd;
132         if (p->mask)
133             no++;
134         if (p->mask & YAZ_SOCKET_OBSERVE_READ)
135         {
136             yaz_log (m_log, "Yaz_SocketManager::select fd=%d read", fd);
137             FD_SET(fd, &in);
138         }
139         if (p->mask & YAZ_SOCKET_OBSERVE_WRITE)
140         {
141             yaz_log (m_log, "Yaz_SocketManager::select fd=%d write", fd);
142             FD_SET(fd, &out);
143         }
144         if (p->mask & YAZ_SOCKET_OBSERVE_EXCEPT)
145         {
146             yaz_log (m_log, "Yaz_SocketManager::select fd=%d except", fd);
147             FD_SET(fd, &except);
148         }
149         if (fd > max)
150             max = fd;
151         if (p->timeout > 0 ||
152             (p->timeout == 0 && (p->mask & YAZ_SOCKET_OBSERVE_WRITE) == 0))
153         {
154             int timeout_this;
155             timeout_this = p->timeout;
156             if (p->last_activity)
157                 timeout_this -= now - p->last_activity;
158             else
159                 p->last_activity = now;
160             if (timeout_this < 0 || timeout_this > 2147483646)
161                 timeout_this = 0;
162             if (timeout == -1 || timeout_this < timeout)
163                 timeout = timeout_this;
164             p->timeout_this = timeout_this;
165             yaz_log (m_log, "Yaz_SocketManager::select timeout_this=%d", 
166                      p->timeout_this);
167         }
168     }
169     if (!no)
170     {
171         yaz_log (m_log, "no pending events return 0");
172         if (!m_observers)
173             yaz_log (m_log, "no observers");
174         return 0;
175     }
176
177     struct timeval to;
178     to.tv_sec = timeout;
179     to.tv_usec = 0;
180     
181     yaz_log (m_log, "Yaz_SocketManager::select begin no=%d timeout=%d",
182              no, timeout);
183     int pass = 0;
184     while ((res = select(max + 1, &in, &out, &except,
185                          timeout== -1 ? 0 : &to)) < 0)
186         if (errno != EINTR)
187         {
188             yaz_log(YLOG_ERRNO|YLOG_WARN, "select");
189             yaz_log(YLOG_WARN, "errno=%d max=%d timeout=%d",
190                              errno, max, timeout);
191             if (++pass > 10)
192                 return -1;
193         }
194     yaz_log(m_log, "select returned res=%d", res);
195     now = time(0);
196     for (p = m_observers; p; p = p->next)
197     {
198         int fd = p->fd;
199         int mask = 0;
200         if (FD_ISSET(fd, &in))
201             mask |= YAZ_SOCKET_OBSERVE_READ;
202
203         if (FD_ISSET(fd, &out))
204             mask |= YAZ_SOCKET_OBSERVE_WRITE;
205
206         if (FD_ISSET(fd, &except))
207             mask |= YAZ_SOCKET_OBSERVE_EXCEPT;
208         
209         if (mask)
210         {
211             YazSocketEvent *event = new YazSocketEvent;
212             p->last_activity = now;
213             event->observer = p->observer;
214             event->event = mask;
215             putEvent (event);
216
217             yaz_log (m_log, "putEvent I/O mask=%d", mask);
218         }
219         else if (res == 0 && p->timeout_this == timeout)
220         {
221             YazSocketEvent *event = new YazSocketEvent;
222             assert (p->last_activity);
223             yaz_log (m_log, "putEvent timeout fd=%d, now = %ld last_activity=%ld timeout=%d",
224                      p->fd, now, p->last_activity, p->timeout);
225             p->last_activity = now;
226             event->observer = p->observer;
227             event->event = YAZ_SOCKET_OBSERVE_TIMEOUT;
228             putEvent (event);
229         }
230     }
231     if ((event = getEvent()))
232     {
233         event->observer->socketNotify(event->event);
234         delete event;
235         return 1;
236     }
237     yaz_log(YLOG_WARN, "unhandled event in processEvent res=%d", res);
238     return 1;
239 }
240
241
242 //    n p    n p  ......   n p    n p
243 //   front                        back
244
245 void Yaz_SocketManager::putEvent(YazSocketEvent *event)
246 {
247     // put in back of queue
248     if (m_queue_back)
249     {
250         m_queue_back->prev = event;
251         assert (m_queue_front);
252     }
253     else
254     {
255         assert (!m_queue_front);
256         m_queue_front = event;
257     }
258     event->next = m_queue_back;
259     event->prev = 0;
260     m_queue_back = event;
261 }
262
263 Yaz_SocketManager::YazSocketEvent *Yaz_SocketManager::getEvent()
264 {
265     // get from front of queue
266     YazSocketEvent *event = m_queue_front;
267     if (!event)
268         return 0;
269     assert (m_queue_back);
270     m_queue_front = event->prev;
271     if (m_queue_front)
272     {
273         assert (m_queue_back);
274         m_queue_front->next = 0;
275     }
276     else
277         m_queue_back = 0;
278     return event;
279 }
280
281 void Yaz_SocketManager::removeEvent(IYazSocketObserver *observer)
282 {
283     YazSocketEvent *ev = m_queue_back;
284     while (ev)
285     {
286         YazSocketEvent *ev_next = ev->next;
287         if (observer == ev->observer)
288         {
289             if (ev->prev)
290                 ev->prev->next = ev->next;
291             else
292                 m_queue_back = ev->next;
293             if (ev->next)
294                 ev->next->prev = ev->prev;
295             else
296                 m_queue_front = ev->prev;
297             delete ev;
298         }
299         ev = ev_next;
300     }
301 }
302
303 Yaz_SocketManager::Yaz_SocketManager()
304 {
305     m_observers = 0;
306     m_queue_front = 0;
307     m_queue_back = 0;
308     m_log = YLOG_DEBUG;
309 }
310
311 Yaz_SocketManager::~Yaz_SocketManager()
312 {
313     deleteObservers();
314 }