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