The upgrade to automake 1.8/1.9 causes the product dist to be called
[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.36 2006-03-29 13:14:17 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 <yazpp/socket-manager.h>
27
28 using namespace yazpp_1;
29
30 SocketManager::SocketEntry **SocketManager::lookupObserver(
31     ISocketObserver *observer)
32 {
33     SocketEntry **se;
34     
35     for (se = &m_observers; *se; se = &(*se)->next)
36         if ((*se)->observer == observer)
37             break;
38     return se;
39 }
40
41 void SocketManager::addObserver(int fd, ISocketObserver *observer)
42 {
43     SocketEntry *se;
44
45     se = *lookupObserver(observer);
46     if (!se)
47     {
48         se = new SocketEntry;
49         se->next= m_observers;
50         m_observers = se;
51         se->observer = observer;
52     }
53     se->fd = fd;
54     se->mask = 0;
55     se->last_activity = 0;
56     se->timeout = -1;
57 }
58
59 void SocketManager::deleteObserver(ISocketObserver *observer)
60 {
61     SocketEntry **se = lookupObserver(observer);
62     if (*se)
63     {
64         removeEvent (observer);
65         SocketEntry *se_tmp = *se;
66         *se = (*se)->next;
67         delete se_tmp;
68     }
69 }
70
71 void SocketManager::deleteObservers()
72 {
73     SocketEntry *se = m_observers;
74     
75     while (se)
76     {
77         SocketEntry *se_next = se->next;
78         delete se;
79         se = se_next;
80     }
81     m_observers = 0;
82 }
83
84 void SocketManager::maskObserver(ISocketObserver *observer, int mask)
85 {
86     SocketEntry *se;
87
88     yaz_log(m_log, "obs=%p read=%d write=%d except=%d", observer,
89                     mask & SOCKET_OBSERVE_READ,
90                     mask & SOCKET_OBSERVE_WRITE,
91                     mask & SOCKET_OBSERVE_EXCEPT);
92
93     se = *lookupObserver(observer);
94     if (se)
95         se->mask = mask;
96 }
97
98 void SocketManager::timeoutObserver(ISocketObserver *observer,
99                                         int timeout)
100 {
101     SocketEntry *se;
102
103     se = *lookupObserver(observer);
104     if (se)
105         se->timeout = timeout;
106 }
107
108 int SocketManager::processEvent()
109 {
110     SocketEntry *p;
111     SocketEvent *event = getEvent();
112     int timeout = -1;
113     yaz_log (m_log, "SocketManager::processEvent manager=%p", this);
114     if (event)
115     {
116         event->observer->socketNotify(event->event);
117         delete event;
118         return 1;
119     }
120
121     fd_set in, out, except;
122     int res;
123     int max = 0;
124     int no = 0;
125
126     FD_ZERO(&in);
127     FD_ZERO(&out);
128     FD_ZERO(&except);
129
130     time_t now = time(0);
131     for (p = m_observers; p; p = p->next)
132     {
133         int fd = p->fd;
134         if (p->mask)
135             no++;
136         if (p->mask & SOCKET_OBSERVE_READ)
137         {
138             yaz_log (m_log, "SocketManager::select fd=%d read", fd);
139             FD_SET(fd, &in);
140         }
141         if (p->mask & SOCKET_OBSERVE_WRITE)
142         {
143             yaz_log (m_log, "SocketManager::select fd=%d write", fd);
144             FD_SET(fd, &out);
145         }
146         if (p->mask & SOCKET_OBSERVE_EXCEPT)
147         {
148             yaz_log (m_log, "SocketManager::select fd=%d except", fd);
149             FD_SET(fd, &except);
150         }
151         if (fd > max)
152             max = fd;
153         if (p->timeout > 0 ||
154             (p->timeout == 0 && (p->mask & SOCKET_OBSERVE_WRITE) == 0))
155         {
156             int timeout_this;
157             timeout_this = p->timeout;
158             if (p->last_activity)
159                 timeout_this -= now - p->last_activity;
160             else
161                 p->last_activity = now;
162             if (timeout_this < 0 || timeout_this > 2147483646)
163                 timeout_this = 0;
164             if (timeout == -1 || timeout_this < timeout)
165                 timeout = timeout_this;
166             p->timeout_this = timeout_this;
167             yaz_log (m_log, "SocketManager::select timeout_this=%d", 
168                      p->timeout_this);
169         }
170         else
171             p->timeout_this = -1;
172     }
173     if (!no)
174     {
175         yaz_log (m_log, "no pending events return 0");
176         if (!m_observers)
177             yaz_log (m_log, "no observers");
178         return 0;
179     }
180
181     struct timeval to;
182     to.tv_sec = timeout;
183     to.tv_usec = 0;
184     
185     yaz_log (m_log, "SocketManager::select begin no=%d timeout=%d",
186              no, timeout);
187     int pass = 0;
188     while ((res = select(max + 1, &in, &out, &except,
189                          timeout== -1 ? 0 : &to)) < 0)
190         if (errno != EINTR)
191         {
192             yaz_log(YLOG_ERRNO|YLOG_WARN, "select");
193             yaz_log(YLOG_WARN, "errno=%d max=%d timeout=%d",
194                              errno, max, timeout);
195             if (++pass > 10)
196                 return -1;
197         }
198     yaz_log(m_log, "select returned res=%d", res);
199     now = time(0);
200     for (p = m_observers; p; p = p->next)
201     {
202         int fd = p->fd;
203         int mask = 0;
204         if (FD_ISSET(fd, &in))
205             mask |= SOCKET_OBSERVE_READ;
206
207         if (FD_ISSET(fd, &out))
208             mask |= SOCKET_OBSERVE_WRITE;
209
210         if (FD_ISSET(fd, &except))
211             mask |= SOCKET_OBSERVE_EXCEPT;
212         
213         if (mask)
214         {
215             SocketEvent *event = new SocketEvent;
216             p->last_activity = now;
217             event->observer = p->observer;
218             event->event = mask;
219             putEvent (event);
220
221             yaz_log (m_log, "putEvent I/O mask=%d", mask);
222         }
223         else if (res == 0 && p->timeout_this == timeout)
224         {
225             SocketEvent *event = new SocketEvent;
226             assert (p->last_activity);
227             yaz_log (m_log, "putEvent timeout fd=%d, now = %ld last_activity=%ld timeout=%d",
228                      p->fd, now, p->last_activity, p->timeout);
229             p->last_activity = now;
230             event->observer = p->observer;
231             event->event = SOCKET_OBSERVE_TIMEOUT;
232             putEvent (event);
233         }
234     }
235     if ((event = getEvent()))
236     {
237         event->observer->socketNotify(event->event);
238         delete event;
239         return 1;
240     }
241     yaz_log(YLOG_WARN, "unhandled event in processEvent res=%d", res);
242     return 1;
243 }
244
245
246 //    n p    n p  ......   n p    n p
247 //   front                        back
248
249 void SocketManager::putEvent(SocketEvent *event)
250 {
251     // put in back of queue
252     if (m_queue_back)
253     {
254         m_queue_back->prev = event;
255         assert (m_queue_front);
256     }
257     else
258     {
259         assert (!m_queue_front);
260         m_queue_front = event;
261     }
262     event->next = m_queue_back;
263     event->prev = 0;
264     m_queue_back = event;
265 }
266
267 SocketManager::SocketEvent *SocketManager::getEvent()
268 {
269     // get from front of queue
270     SocketEvent *event = m_queue_front;
271     if (!event)
272         return 0;
273     assert (m_queue_back);
274     m_queue_front = event->prev;
275     if (m_queue_front)
276     {
277         assert (m_queue_back);
278         m_queue_front->next = 0;
279     }
280     else
281         m_queue_back = 0;
282     return event;
283 }
284
285 void SocketManager::removeEvent(ISocketObserver *observer)
286 {
287     SocketEvent *ev = m_queue_back;
288     while (ev)
289     {
290         SocketEvent *ev_next = ev->next;
291         if (observer == ev->observer)
292         {
293             if (ev->prev)
294                 ev->prev->next = ev->next;
295             else
296                 m_queue_back = ev->next;
297             if (ev->next)
298                 ev->next->prev = ev->prev;
299             else
300                 m_queue_front = ev->prev;
301             delete ev;
302         }
303         ev = ev_next;
304     }
305 }
306
307 SocketManager::SocketManager()
308 {
309     m_observers = 0;
310     m_queue_front = 0;
311     m_queue_back = 0;
312     m_log = YLOG_DEBUG;
313 }
314
315 SocketManager::~SocketManager()
316 {
317     deleteObservers();
318 }
319 /*
320  * Local variables:
321  * c-basic-offset: 4
322  * indent-tabs-mode: nil
323  * End:
324  * vim: shiftwidth=4 tabstop=8 expandtab
325  */
326