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