Bump year
[yaz-moved-to-github.git] / src / eventl.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: eventl.c,v 1.5 2005-01-15 19:47:13 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 <stdio.h>
17 #include <assert.h>
18 #ifdef WIN32
19 #include <winsock.h>
20 #else
21 #include <unistd.h>
22 #endif
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>
26
27 #include <yaz/yconfig.h>
28 #include <yaz/log.h>
29 #include <yaz/comstack.h>
30 #include <yaz/xmalloc.h>
31 #include "eventl.h"
32 #include "session.h"
33 #include <yaz/statserv.h>
34
35 #if YAZ_GNU_THREADS
36 #include <pth.h>
37 #define YAZ_EV_SELECT pth_select
38 #endif
39
40 #ifndef YAZ_EV_SELECT
41 #define YAZ_EV_SELECT select
42 #endif
43
44 static int log_level=0;
45 static int log_level_initialized=0;
46
47 IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags)
48 {
49     IOCHAN new_iochan;
50
51     if (!log_level_initialized)
52     {
53         log_level=yaz_log_module_level("eventl");
54         log_level_initialized=1;
55     }
56
57     if (!(new_iochan = (IOCHAN)xmalloc(sizeof(*new_iochan))))
58         return 0;
59     new_iochan->destroyed = 0;
60     new_iochan->fd = fd;
61     new_iochan->flags = flags;
62     new_iochan->fun = cb;
63     new_iochan->force_event = 0;
64     new_iochan->last_event = new_iochan->max_idle = 0;
65     new_iochan->next = NULL;
66     return new_iochan;
67 }
68
69 int event_loop(IOCHAN *iochans)
70 {
71     do /* loop as long as there are active associations to process */
72     {
73         IOCHAN p, nextp;
74         fd_set in, out, except;
75         int res, max;
76         static struct timeval to;
77         time_t now = time(0);
78
79         if (statserv_must_terminate())
80         {
81             for (p = *iochans; p; p = p->next)
82                 p->force_event = EVENT_TIMEOUT;
83         }
84         FD_ZERO(&in);
85         FD_ZERO(&out);
86         FD_ZERO(&except);
87         to.tv_sec = 3600;
88         to.tv_usec = 0;
89         max = 0;
90         for (p = *iochans; p; p = p->next)
91         {
92             time_t w, ftime;
93             yaz_log(log_level, "fd=%d flags=%d force_event=%d",
94                     p->fd, p->flags, p->force_event);
95             if (p->force_event)
96                 to.tv_sec = 0;          /* polling select */
97             if (p->flags & EVENT_INPUT)
98                 FD_SET(p->fd, &in);
99             if (p->flags & EVENT_OUTPUT)
100                 FD_SET(p->fd, &out);
101             if (p->flags & EVENT_EXCEPT)
102                 FD_SET(p->fd, &except);
103             if (p->fd > max)
104                 max = p->fd;
105             if (p->max_idle && p->last_event)
106             {
107                 ftime = p->last_event + p->max_idle;
108                 if (ftime < now)
109                     w = p->max_idle;
110                 else
111                     w = ftime - now;
112                 if (w < to.tv_sec)
113                     to.tv_sec = w;
114             }
115         }
116         yaz_log(log_level, "select start %ld", (long) to.tv_sec);
117         res = YAZ_EV_SELECT(max + 1, &in, &out, &except, &to);
118         yaz_log(log_level, "select end");
119         if (res < 0)
120         {
121             if (yaz_errno() == EINTR)
122             {
123                 if (statserv_must_terminate())
124                 {
125                     for (p = *iochans; p; p = p->next)
126                         p->force_event = EVENT_TIMEOUT;
127                 }
128                 continue;
129             }
130             else
131             {
132                 /* Destroy the first member in the chain, and try again */
133                 association *assoc = (association *)iochan_getdata(*iochans);
134                 COMSTACK conn = assoc->client_link;
135
136                 cs_close(conn);
137                 destroy_association(assoc);
138                 iochan_destroy(*iochans);
139                 yaz_log(log_level, "error select, destroying iochan %p",
140                         *iochans);
141             }
142         }
143         now = time(0);
144         for (p = *iochans; p; p = p->next)
145         {
146             int force_event = p->force_event;
147
148             p->force_event = 0;
149             if (!p->destroyed && (FD_ISSET(p->fd, &in) ||
150                 force_event == EVENT_INPUT))
151             {
152                 p->last_event = now;
153                 (*p->fun)(p, EVENT_INPUT);
154             }
155             if (!p->destroyed && (FD_ISSET(p->fd, &out) ||
156                 force_event == EVENT_OUTPUT))
157             {
158                 p->last_event = now;
159                 (*p->fun)(p, EVENT_OUTPUT);
160             }
161             if (!p->destroyed && (FD_ISSET(p->fd, &except) ||
162                 force_event == EVENT_EXCEPT))
163             {
164                 p->last_event = now;
165                 (*p->fun)(p, EVENT_EXCEPT);
166             }
167             if (!p->destroyed && ((p->max_idle && now - p->last_event >=
168                 p->max_idle) || force_event == EVENT_TIMEOUT))
169             {
170                 p->last_event = now;
171                 (*p->fun)(p, EVENT_TIMEOUT);
172             }
173         }
174         for (p = *iochans; p; p = nextp)
175         {
176             nextp = p->next;
177
178             if (p->destroyed)
179             {
180                 IOCHAN tmp = p, pr;
181
182                 /* We need to inform the threadlist that this channel has been destroyed */
183                 statserv_remove(p);
184
185                 /* Now reset the pointers */
186                 if (p == *iochans)
187                     *iochans = p->next;
188                 else
189                 {
190                     for (pr = *iochans; pr; pr = pr->next)
191                         if (pr->next == p)
192                             break;
193                     assert(pr); /* grave error if it weren't there */
194                     pr->next = p->next;
195                 }
196                 if (nextp == p)
197                     nextp = p->next;
198                 xfree(tmp);
199             }
200         }
201     }
202     while (*iochans);
203     return 0;
204 }