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