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