Updated RPM spec file with some enhancements from Martin Armbrecht.
[yaz-moved-to-github.git] / src / eventl.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: eventl.c,v 1.11 2007-01-03 08:42:15 adam Exp $
6  */
7
8 /**
9  * \file eventl.c
10  * \brief Implements event loop handling for GFS.
11  *
12  * This source implements the main event loop for the Generic Frontend
13  * Server. It uses select(2).
14  */
15
16 #include <assert.h>
17 #include <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #if HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25 #if HAVE_SYS_TIME_H
26 #include <sys/time.h>
27 #endif
28 #ifdef WIN32
29 #include <winsock.h>
30 #endif
31 #if HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #if HAVE_SYS_SELECT_H
35 #include <sys/select.h>
36 #endif
37
38 #include <yaz/yconfig.h>
39 #include <yaz/log.h>
40 #include <yaz/comstack.h>
41 #include <yaz/xmalloc.h>
42 #include "eventl.h"
43 #include "session.h"
44 #include <yaz/statserv.h>
45
46 #if YAZ_GNU_THREADS
47 #include <pth.h>
48 #define YAZ_EV_SELECT pth_select
49 #endif
50
51 #ifndef YAZ_EV_SELECT
52 #define YAZ_EV_SELECT select
53 #endif
54
55 static int log_level=0;
56 static int log_level_initialized=0;
57
58 IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, int chan_id)
59 {
60     IOCHAN new_iochan;
61
62     if (!log_level_initialized)
63     {
64         log_level=yaz_log_module_level("eventl");
65         log_level_initialized=1;
66     }
67
68     if (!(new_iochan = (IOCHAN)xmalloc(sizeof(*new_iochan))))
69         return 0;
70     new_iochan->destroyed = 0;
71     new_iochan->fd = fd;
72     new_iochan->flags = flags;
73     new_iochan->fun = cb;
74     new_iochan->force_event = 0;
75     new_iochan->last_event = new_iochan->max_idle = 0;
76     new_iochan->next = NULL;
77     new_iochan->chan_id = chan_id;
78     return new_iochan;
79 }
80
81 int iochan_is_alive(IOCHAN chan)
82 {
83     static struct timeval to;
84     fd_set in, out, except;
85     int res, max;
86
87     to.tv_sec = 0;
88     to.tv_usec = 0;
89
90     FD_ZERO(&in);
91     FD_ZERO(&out);
92     FD_ZERO(&except);
93
94     FD_SET(chan->fd, &in);
95
96     max = chan->fd + 1;
97
98     res = YAZ_EV_SELECT(max + 1, &in, 0, 0, &to);
99     if (res == 0)
100         return 1;
101     if (!ir_read(chan, EVENT_INPUT))
102         return 0;
103     return 1;
104 }
105
106 int event_loop(IOCHAN *iochans)
107 {
108     do /* loop as long as there are active associations to process */
109     {
110         IOCHAN p, nextp;
111         fd_set in, out, except;
112         int res, max;
113         static struct timeval to;
114         time_t now = time(0);
115
116         if (statserv_must_terminate())
117         {
118             for (p = *iochans; p; p = p->next)
119                 p->force_event = EVENT_TIMEOUT;
120         }
121         FD_ZERO(&in);
122         FD_ZERO(&out);
123         FD_ZERO(&except);
124         to.tv_sec = 3600;
125         to.tv_usec = 0;
126         max = 0;
127         for (p = *iochans; p; p = p->next)
128         {
129             time_t w, ftime;
130             yaz_log(log_level, "fd=%d flags=%d force_event=%d",
131                     p->fd, p->flags, p->force_event);
132             if (p->force_event)
133                 to.tv_sec = 0;          /* polling select */
134             if (p->flags & EVENT_INPUT)
135                 FD_SET(p->fd, &in);
136             if (p->flags & EVENT_OUTPUT)
137                 FD_SET(p->fd, &out);
138             if (p->flags & EVENT_EXCEPT)
139                 FD_SET(p->fd, &except);
140             if (p->fd > max)
141                 max = p->fd;
142             if (p->max_idle && p->last_event)
143             {
144                 ftime = p->last_event + p->max_idle;
145                 if (ftime < now)
146                     w = p->max_idle;
147                 else
148                     w = ftime - now;
149                 if (w < to.tv_sec)
150                     to.tv_sec = w;
151             }
152         }
153         yaz_log(log_level, "select start %ld", (long) to.tv_sec);
154         res = YAZ_EV_SELECT(max + 1, &in, &out, &except, &to);
155         yaz_log(log_level, "select end");
156         if (res < 0)
157         {
158             if (yaz_errno() == EINTR)
159             {
160                 if (statserv_must_terminate())
161                 {
162                     for (p = *iochans; p; p = p->next)
163                         p->force_event = EVENT_TIMEOUT;
164                 }
165                 continue;
166             }
167             else
168             {
169                 /* Destroy the first member in the chain, and try again */
170                 association *assoc = (association *)iochan_getdata(*iochans);
171                 COMSTACK conn = assoc->client_link;
172
173                 cs_close(conn);
174                 destroy_association(assoc);
175                 iochan_destroy(*iochans);
176                 yaz_log(log_level, "error select, destroying iochan %p",
177                         *iochans);
178             }
179         }
180         now = time(0);
181         for (p = *iochans; p; p = p->next)
182         {
183             int force_event = p->force_event;
184
185             p->force_event = 0;
186             if (!p->destroyed && (FD_ISSET(p->fd, &in) ||
187                 force_event == EVENT_INPUT))
188             {
189                 p->last_event = now;
190                 (*p->fun)(p, EVENT_INPUT);
191             }
192             if (!p->destroyed && (FD_ISSET(p->fd, &out) ||
193                 force_event == EVENT_OUTPUT))
194             {
195                 p->last_event = now;
196                 (*p->fun)(p, EVENT_OUTPUT);
197             }
198             if (!p->destroyed && (FD_ISSET(p->fd, &except) ||
199                 force_event == EVENT_EXCEPT))
200             {
201                 p->last_event = now;
202                 (*p->fun)(p, EVENT_EXCEPT);
203             }
204             if (!p->destroyed && ((p->max_idle && now - p->last_event >=
205                 p->max_idle) || force_event == EVENT_TIMEOUT))
206             {
207                 p->last_event = now;
208                 (*p->fun)(p, EVENT_TIMEOUT);
209             }
210         }
211         for (p = *iochans; p; p = nextp)
212         {
213             nextp = p->next;
214
215             if (p->destroyed)
216             {
217                 IOCHAN tmp = p, pr;
218
219                 /* We need to inform the threadlist that this channel has been destroyed */
220                 statserv_remove(p);
221
222                 /* Now reset the pointers */
223                 if (p == *iochans)
224                     *iochans = p->next;
225                 else
226                 {
227                     for (pr = *iochans; pr; pr = pr->next)
228                         if (pr->next == p)
229                             break;
230                     assert(pr); /* grave error if it weren't there */
231                     pr->next = p->next;
232                 }
233                 if (nextp == p)
234                     nextp = p->next;
235                 xfree(tmp);
236             }
237         }
238     }
239     while (*iochans);
240     return 0;
241 }
242 /*
243  * Local variables:
244  * c-basic-offset: 4
245  * indent-tabs-mode: nil
246  * End:
247  * vim: shiftwidth=4 tabstop=8 expandtab
248  */
249