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