Yet another WIN32 fix for connect notify.
[yazpp-moved-to-github.git] / src / yaz-socket-manager.cpp
1 /*
2  * Copyright (c) 1998-2000, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Log: yaz-socket-manager.cpp,v $
6  * Revision 1.14  2000-11-20 14:17:36  adam
7  * Yet another WIN32 fix for connect notify.
8  *
9  * Revision 1.13  2000/11/20 11:27:33  adam
10  * Fixes for connect operation (timeout and notify fix).
11  *
12  * Revision 1.12  2000/10/24 12:29:57  adam
13  * Fixed bug in proxy where a Yaz_ProxyClient could be owned by
14  * two Yaz_Proxy's (fatal).
15  *
16  * Revision 1.11  2000/10/11 11:58:17  adam
17  * Moved header files to include/yaz++. Switched to libtool and automake.
18  * Configure script creates yaz++-config script.
19  *
20  * Revision 1.10  2000/09/08 10:23:42  adam
21  * Added skeleton of yaz-z-server.
22  *
23  * Revision 1.9  2000/08/07 14:19:59  adam
24  * Fixed serious bug regarding timeouts. Improved logging for proxy.
25  *
26  * Revision 1.8  1999/12/06 13:52:45  adam
27  * Modified for new location of YAZ header files. Experimental threaded
28  * operation.
29  *
30  * Revision 1.7  1999/04/28 13:02:08  adam
31  * Added include of string.h.
32  *
33  * Revision 1.6  1999/04/21 12:09:01  adam
34  * Many improvements. Modified to proxy server to work with "sessions"
35  * based on cookies.
36  *
37  * Revision 1.5  1999/04/09 11:46:57  adam
38  * Added object Yaz_Z_Assoc. Much more functional client.
39  *
40  * Revision 1.4  1999/03/23 14:17:57  adam
41  * More work on timeout handling. Work on yaz-client.
42  *
43  * Revision 1.3  1999/02/02 14:01:23  adam
44  * First WIN32 port of YAZ++.
45  *
46  * Revision 1.2  1999/01/28 13:08:48  adam
47  * Yaz_PDU_Assoc better encapsulated. Memory leak fix in
48  * yaz-socket-manager.cc.
49  *
50  * Revision 1.1.1.1  1999/01/28 09:41:07  adam
51  * First implementation of YAZ++.
52  *
53  */
54 #include <assert.h>
55 #ifdef WIN32
56 #include <winsock.h>
57 #else
58 #include <sys/time.h>
59 #include <sys/types.h>
60 #include <unistd.h>
61 #endif
62 #include <errno.h>
63 #include <string.h>
64
65 #include <yaz/log.h>
66 #include <yaz++/yaz-socket-manager.h>
67
68 Yaz_SocketManager::YazSocketEntry **Yaz_SocketManager::lookupObserver(
69     IYazSocketObserver *observer)
70 {
71     YazSocketEntry **se;
72     
73     for (se = &m_observers; *se; se = &(*se)->next)
74         if ((*se)->observer == observer)
75             break;
76     return se;
77 }
78
79 void Yaz_SocketManager::addObserver(int fd, IYazSocketObserver *observer)
80 {
81     YazSocketEntry *se;
82
83     se = *lookupObserver(observer);
84     if (!se)
85     {
86         se = new YazSocketEntry;
87         se->next= m_observers;
88         m_observers = se;
89         se->observer = observer;
90     }
91     se->fd = fd;
92     se->mask = 0;
93     se->last_activity = 0;
94     se->timeout = 0;
95 }
96
97 void Yaz_SocketManager::deleteObserver(IYazSocketObserver *observer)
98 {
99     YazSocketEntry **se = lookupObserver(observer);
100     if (*se)
101     {
102         removeEvent (observer);
103         YazSocketEntry *se_tmp = *se;
104         *se = (*se)->next;
105         delete se_tmp;
106     }
107 }
108
109 void Yaz_SocketManager::deleteObservers()
110 {
111     YazSocketEntry *se = m_observers;
112     
113     while (se)
114     {
115         YazSocketEntry *se_next = se->next;
116         delete se;
117         se = se_next;
118     }
119     m_observers = 0;
120 }
121
122 void Yaz_SocketManager::maskObserver(IYazSocketObserver *observer, int mask)
123 {
124     YazSocketEntry *se;
125
126     se = *lookupObserver(observer);
127     if (se)
128         se->mask = mask;
129 }
130
131 void Yaz_SocketManager::timeoutObserver(IYazSocketObserver *observer,
132                                         unsigned timeout)
133 {
134     YazSocketEntry *se;
135
136     se = *lookupObserver(observer);
137     if (se)
138         se->timeout = timeout;
139 }
140
141 int Yaz_SocketManager::processEvent()
142 {
143     YazSocketEntry *p;
144     YazSocketEvent *event = getEvent();
145     unsigned timeout = 0;
146     logf (m_log, "processEvent");
147     if (event)
148     {
149         event->observer->socketNotify(event->event);
150         delete event;
151         return 1;
152     }
153
154     fd_set in, out, except;
155     int res;
156     int max = 0;
157     int no = 0;
158
159     FD_ZERO(&in);
160     FD_ZERO(&out);
161     FD_ZERO(&except);
162
163     time_t now = time(0);
164     for (p = m_observers; p; p = p->next)
165     {
166         int fd = p->fd;
167         if (p->mask)
168             no++;
169         if (p->mask & YAZ_SOCKET_OBSERVE_READ)
170         {
171             yaz_log (m_log, "select fd=%d: read fd", fd);
172             FD_SET(fd, &in);
173         }
174         if (p->mask & YAZ_SOCKET_OBSERVE_WRITE)
175         {
176             yaz_log (m_log, "select fd=%d: write fd", fd);
177             FD_SET(fd, &out);
178         }
179         if (p->mask & YAZ_SOCKET_OBSERVE_EXCEPT)
180         {
181             yaz_log (m_log, "select fd=%d: except fd", fd);
182             FD_SET(fd, &except);
183         }
184         if (fd > max)
185             max = fd;
186         if (p->timeout)
187         {
188             unsigned timeout_this;
189             timeout_this = p->timeout;
190             if (p->last_activity)
191                 timeout_this -= now - p->last_activity;
192             else
193                 p->last_activity = now;
194             if (timeout_this < 1)
195                 timeout_this = 1;
196             if (!timeout || timeout_this < timeout)
197                 timeout = timeout_this;
198         }
199     }
200     if (!no)
201     {
202         logf (m_log, "no pending events return 0");
203         if (!m_observers)
204             logf (m_log, "no observers");
205         return 0;
206     }
207
208     struct timeval to;
209     to.tv_sec = timeout;
210     to.tv_usec = 0;
211     
212     logf (m_log, "select pending=%d timeout=%d", no, timeout);
213     while ((res = select(max + 1, &in, &out, &except, timeout ? &to : 0)) < 0)
214         if (errno != EINTR)
215             return -1;
216     now = time(0);
217     for (p = m_observers; p; p = p->next)
218     {
219         int fd = p->fd;
220         int mask = 0;
221         if (FD_ISSET(fd, &in))
222             mask |= YAZ_SOCKET_OBSERVE_READ;
223
224         if (FD_ISSET(fd, &out))
225             mask |= YAZ_SOCKET_OBSERVE_WRITE;
226
227         if (FD_ISSET(fd, &except))
228             mask |= YAZ_SOCKET_OBSERVE_EXCEPT;
229         
230         if (mask)
231         {
232             YazSocketEvent *event = new YazSocketEvent;
233             p->last_activity = now;
234             event->observer = p->observer;
235             event->event = mask;
236             putEvent (event);
237         }
238         else if (p->timeout && p->last_activity && 
239                  now >= p->last_activity + (int) (p->timeout))
240         {
241             YazSocketEvent *event = new YazSocketEvent;
242             logf (m_log, "timeout, now = %ld last_activity=%ld timeout=%d",
243                   now, p->last_activity, p->timeout);
244             p->last_activity = now;
245             event->observer = p->observer;
246             event->event = YAZ_SOCKET_OBSERVE_TIMEOUT;
247             putEvent (event);
248         }
249     }
250     if ((event = getEvent()))
251     {
252         event->observer->socketNotify(event->event);
253         delete event;
254         return 1;
255     }
256     return 0;
257 }
258
259
260 //    n p    n p  ......   n p    n p
261 //   front                        back
262
263 void Yaz_SocketManager::putEvent(YazSocketEvent *event)
264 {
265     // put in back of queue
266     if (m_queue_back)
267     {
268         m_queue_back->prev = event;
269         assert (m_queue_front);
270     }
271     else
272     {
273         assert (!m_queue_front);
274         m_queue_front = event;
275     }
276     event->next = m_queue_back;
277     event->prev = 0;
278     m_queue_back = event;
279 }
280
281 Yaz_SocketManager::YazSocketEvent *Yaz_SocketManager::getEvent()
282 {
283     // get from front of queue
284     YazSocketEvent *event = m_queue_front;
285     if (!event)
286         return 0;
287     assert (m_queue_back);
288     m_queue_front = event->prev;
289     if (m_queue_front)
290     {
291         assert (m_queue_back);
292         m_queue_front->next = 0;
293     }
294     else
295         m_queue_back = 0;
296     return event;
297 }
298
299 void Yaz_SocketManager::removeEvent(IYazSocketObserver *observer)
300 {
301     YazSocketEvent *ev = m_queue_back;
302     while (ev)
303     {
304         YazSocketEvent *ev_next = ev->next;
305         if (observer == ev->observer)
306         {
307             if (ev->prev)
308                 ev->prev->next = ev->next;
309             else
310                 m_queue_back = ev->next;
311             if (ev->next)
312                 ev->next->prev = ev->prev;
313             else
314                 m_queue_front = ev->prev;
315             delete ev;
316         }
317         ev = ev_next;
318     }
319 }
320
321 Yaz_SocketManager::Yaz_SocketManager()
322 {
323     m_observers = 0;
324     m_queue_front = 0;
325     m_queue_back = 0;
326     m_log = LOG_DEBUG;
327 }
328
329 Yaz_SocketManager::~Yaz_SocketManager()
330 {
331     deleteObservers();
332 }