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