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