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