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