1*12Sigor@sysoev.ru 
2*12Sigor@sysoev.ru /*
3*12Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4*12Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5*12Sigor@sysoev.ru  */
6*12Sigor@sysoev.ru 
7*12Sigor@sysoev.ru #include <nxt_main.h>
8*12Sigor@sysoev.ru 
9*12Sigor@sysoev.ru 
10*12Sigor@sysoev.ru /*
11*12Sigor@sysoev.ru  * The event ports have been introduced in Solaris 10.
12*12Sigor@sysoev.ru  * The PORT_SOURCE_MQ and PORT_SOURCE_FILE sources have
13*12Sigor@sysoev.ru  * been added in OpenSolaris.
14*12Sigor@sysoev.ru  */
15*12Sigor@sysoev.ru 
16*12Sigor@sysoev.ru 
17*12Sigor@sysoev.ru static nxt_int_t nxt_eventport_create(nxt_event_engine_t *engine,
18*12Sigor@sysoev.ru     nxt_uint_t mchanges, nxt_uint_t mevents);
19*12Sigor@sysoev.ru static void nxt_eventport_free(nxt_event_engine_t *engine);
20*12Sigor@sysoev.ru static void nxt_eventport_enable(nxt_event_engine_t *engine,
21*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
22*12Sigor@sysoev.ru static void nxt_eventport_disable(nxt_event_engine_t *engine,
23*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
24*12Sigor@sysoev.ru static nxt_bool_t nxt_eventport_close(nxt_event_engine_t *engine,
25*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
26*12Sigor@sysoev.ru static void nxt_eventport_enable_read(nxt_event_engine_t *engine,
27*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
28*12Sigor@sysoev.ru static void nxt_eventport_enable_write(nxt_event_engine_t *engine,
29*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
30*12Sigor@sysoev.ru static void nxt_eventport_enable_event(nxt_event_engine_t *engine,
31*12Sigor@sysoev.ru     nxt_fd_event_t *ev, nxt_uint_t events);
32*12Sigor@sysoev.ru static void nxt_eventport_disable_read(nxt_event_engine_t *engine,
33*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
34*12Sigor@sysoev.ru static void nxt_eventport_disable_write(nxt_event_engine_t *engine,
35*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
36*12Sigor@sysoev.ru static void nxt_eventport_disable_event(nxt_event_engine_t *engine,
37*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
38*12Sigor@sysoev.ru static nxt_int_t nxt_eventport_commit_changes(nxt_event_engine_t *engine);
39*12Sigor@sysoev.ru static void nxt_eventport_error_handler(nxt_task_t *task, void *obj,
40*12Sigor@sysoev.ru     void *data);
41*12Sigor@sysoev.ru static void nxt_eventport_block_read(nxt_event_engine_t *engine,
42*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
43*12Sigor@sysoev.ru static void nxt_eventport_block_write(nxt_event_engine_t *engine,
44*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
45*12Sigor@sysoev.ru static void nxt_eventport_oneshot_read(nxt_event_engine_t *engine,
46*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
47*12Sigor@sysoev.ru static void nxt_eventport_oneshot_write(nxt_event_engine_t *engine,
48*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
49*12Sigor@sysoev.ru static void nxt_eventport_enable_accept(nxt_event_engine_t *engine,
50*12Sigor@sysoev.ru     nxt_fd_event_t *ev);
51*12Sigor@sysoev.ru static nxt_int_t nxt_eventport_enable_post(nxt_event_engine_t *engine,
52*12Sigor@sysoev.ru     nxt_work_handler_t handler);
53*12Sigor@sysoev.ru static void nxt_eventport_signal(nxt_event_engine_t *engine, nxt_uint_t signo);
54*12Sigor@sysoev.ru static void nxt_eventport_poll(nxt_event_engine_t *engine,
55*12Sigor@sysoev.ru     nxt_msec_t timeout);
56*12Sigor@sysoev.ru 
57*12Sigor@sysoev.ru 
58*12Sigor@sysoev.ru const nxt_event_interface_t  nxt_eventport_engine = {
59*12Sigor@sysoev.ru     "eventport",
60*12Sigor@sysoev.ru     nxt_eventport_create,
61*12Sigor@sysoev.ru     nxt_eventport_free,
62*12Sigor@sysoev.ru     nxt_eventport_enable,
63*12Sigor@sysoev.ru     nxt_eventport_disable,
64*12Sigor@sysoev.ru     nxt_eventport_disable,
65*12Sigor@sysoev.ru     nxt_eventport_close,
66*12Sigor@sysoev.ru     nxt_eventport_enable_read,
67*12Sigor@sysoev.ru     nxt_eventport_enable_write,
68*12Sigor@sysoev.ru     nxt_eventport_disable_read,
69*12Sigor@sysoev.ru     nxt_eventport_disable_write,
70*12Sigor@sysoev.ru     nxt_eventport_block_read,
71*12Sigor@sysoev.ru     nxt_eventport_block_write,
72*12Sigor@sysoev.ru     nxt_eventport_oneshot_read,
73*12Sigor@sysoev.ru     nxt_eventport_oneshot_write,
74*12Sigor@sysoev.ru     nxt_eventport_enable_accept,
75*12Sigor@sysoev.ru     NULL,
76*12Sigor@sysoev.ru     NULL,
77*12Sigor@sysoev.ru     nxt_eventport_enable_post,
78*12Sigor@sysoev.ru     nxt_eventport_signal,
79*12Sigor@sysoev.ru     nxt_eventport_poll,
80*12Sigor@sysoev.ru 
81*12Sigor@sysoev.ru     &nxt_unix_event_conn_io,
82*12Sigor@sysoev.ru 
83*12Sigor@sysoev.ru     NXT_NO_FILE_EVENTS,
84*12Sigor@sysoev.ru     NXT_NO_SIGNAL_EVENTS,
85*12Sigor@sysoev.ru };
86*12Sigor@sysoev.ru 
87*12Sigor@sysoev.ru 
88*12Sigor@sysoev.ru static nxt_int_t
89*12Sigor@sysoev.ru nxt_eventport_create(nxt_event_engine_t *engine, nxt_uint_t mchanges,
90*12Sigor@sysoev.ru     nxt_uint_t mevents)
91*12Sigor@sysoev.ru {
92*12Sigor@sysoev.ru     nxt_eventport_change_t  *changes;
93*12Sigor@sysoev.ru 
94*12Sigor@sysoev.ru     engine->u.eventport.fd = -1;
95*12Sigor@sysoev.ru     engine->u.eventport.mchanges = mchanges;
96*12Sigor@sysoev.ru     engine->u.eventport.mevents = mevents;
97*12Sigor@sysoev.ru 
98*12Sigor@sysoev.ru     changes = nxt_malloc(sizeof(nxt_eventport_change_t) * mchanges);
99*12Sigor@sysoev.ru     if (changes == NULL) {
100*12Sigor@sysoev.ru         goto fail;
101*12Sigor@sysoev.ru     }
102*12Sigor@sysoev.ru 
103*12Sigor@sysoev.ru     engine->u.eventport.changes = changes;
104*12Sigor@sysoev.ru 
105*12Sigor@sysoev.ru     engine->u.eventport.events = nxt_malloc(sizeof(port_event_t) * mevents);
106*12Sigor@sysoev.ru     if (engine->u.eventport.events == NULL) {
107*12Sigor@sysoev.ru         goto fail;
108*12Sigor@sysoev.ru     }
109*12Sigor@sysoev.ru 
110*12Sigor@sysoev.ru     engine->u.eventport.fd = port_create();
111*12Sigor@sysoev.ru     if (engine->u.eventport.fd == -1) {
112*12Sigor@sysoev.ru         nxt_log(&engine->task, NXT_LOG_CRIT, "port_create() failed %E",
113*12Sigor@sysoev.ru                 nxt_errno);
114*12Sigor@sysoev.ru         goto fail;
115*12Sigor@sysoev.ru     }
116*12Sigor@sysoev.ru 
117*12Sigor@sysoev.ru     nxt_debug(&engine->task, "port_create(): %d", engine->u.eventport.fd);
118*12Sigor@sysoev.ru 
119*12Sigor@sysoev.ru     if (engine->signals != NULL) {
120*12Sigor@sysoev.ru         engine->u.eventport.signal_handler = engine->signals->handler;
121*12Sigor@sysoev.ru     }
122*12Sigor@sysoev.ru 
123*12Sigor@sysoev.ru     return NXT_OK;
124*12Sigor@sysoev.ru 
125*12Sigor@sysoev.ru fail:
126*12Sigor@sysoev.ru 
127*12Sigor@sysoev.ru     nxt_eventport_free(engine);
128*12Sigor@sysoev.ru 
129*12Sigor@sysoev.ru     return NXT_ERROR;
130*12Sigor@sysoev.ru }
131*12Sigor@sysoev.ru 
132*12Sigor@sysoev.ru 
133*12Sigor@sysoev.ru static void
134*12Sigor@sysoev.ru nxt_eventport_free(nxt_event_engine_t *engine)
135*12Sigor@sysoev.ru {
136*12Sigor@sysoev.ru     int  port;
137*12Sigor@sysoev.ru 
138*12Sigor@sysoev.ru     port = engine->u.eventport.fd;
139*12Sigor@sysoev.ru 
140*12Sigor@sysoev.ru     nxt_debug(&engine->task, "eventport %d free", port);
141*12Sigor@sysoev.ru 
142*12Sigor@sysoev.ru     if (port != -1 && close(port) != 0) {
143*12Sigor@sysoev.ru         nxt_log(&engine->task, NXT_LOG_CRIT, "eventport close(%d) failed %E",
144*12Sigor@sysoev.ru                 port, nxt_errno);
145*12Sigor@sysoev.ru     }
146*12Sigor@sysoev.ru 
147*12Sigor@sysoev.ru     nxt_free(engine->u.eventport.events);
148*12Sigor@sysoev.ru 
149*12Sigor@sysoev.ru     nxt_memzero(&engine->u.eventport, sizeof(nxt_eventport_engine_t));
150*12Sigor@sysoev.ru }
151*12Sigor@sysoev.ru 
152*12Sigor@sysoev.ru 
153*12Sigor@sysoev.ru static void
154*12Sigor@sysoev.ru nxt_eventport_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
155*12Sigor@sysoev.ru {
156*12Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
157*12Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
158*12Sigor@sysoev.ru 
159*12Sigor@sysoev.ru     nxt_eventport_enable_event(engine, ev, POLLIN | POLLOUT);
160*12Sigor@sysoev.ru }
161*12Sigor@sysoev.ru 
162*12Sigor@sysoev.ru 
163*12Sigor@sysoev.ru static void
164*12Sigor@sysoev.ru nxt_eventport_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
165*12Sigor@sysoev.ru {
166*12Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE || ev->write != NXT_EVENT_INACTIVE) {
167*12Sigor@sysoev.ru 
168*12Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
169*12Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
170*12Sigor@sysoev.ru 
171*12Sigor@sysoev.ru         nxt_eventport_disable_event(engine, ev);
172*12Sigor@sysoev.ru     }
173*12Sigor@sysoev.ru }
174*12Sigor@sysoev.ru 
175*12Sigor@sysoev.ru 
176*12Sigor@sysoev.ru /*
177*12Sigor@sysoev.ru  * port_dissociate(3):
178*12Sigor@sysoev.ru  *
179*12Sigor@sysoev.ru  *   The association is removed if the owner of the association closes the port.
180*12Sigor@sysoev.ru  */
181*12Sigor@sysoev.ru 
182*12Sigor@sysoev.ru static nxt_bool_t
183*12Sigor@sysoev.ru nxt_eventport_close(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
184*12Sigor@sysoev.ru {
185*12Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
186*12Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
187*12Sigor@sysoev.ru 
188*12Sigor@sysoev.ru     return ev->changing;
189*12Sigor@sysoev.ru }
190*12Sigor@sysoev.ru 
191*12Sigor@sysoev.ru 
192*12Sigor@sysoev.ru static void
193*12Sigor@sysoev.ru nxt_eventport_enable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
194*12Sigor@sysoev.ru {
195*12Sigor@sysoev.ru     nxt_uint_t  events;
196*12Sigor@sysoev.ru 
197*12Sigor@sysoev.ru     if (ev->read != NXT_EVENT_BLOCKED) {
198*12Sigor@sysoev.ru         events = (ev->write == NXT_EVENT_INACTIVE) ? POLLIN
199*12Sigor@sysoev.ru                                                    : (POLLIN | POLLOUT);
200*12Sigor@sysoev.ru         nxt_eventport_enable_event(engine, ev, events);
201*12Sigor@sysoev.ru     }
202*12Sigor@sysoev.ru 
203*12Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
204*12Sigor@sysoev.ru }
205*12Sigor@sysoev.ru 
206*12Sigor@sysoev.ru 
207*12Sigor@sysoev.ru static void
208*12Sigor@sysoev.ru nxt_eventport_enable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
209*12Sigor@sysoev.ru {
210*12Sigor@sysoev.ru     nxt_uint_t  events;
211*12Sigor@sysoev.ru 
212*12Sigor@sysoev.ru     if (ev->write != NXT_EVENT_BLOCKED) {
213*12Sigor@sysoev.ru         events = (ev->read == NXT_EVENT_INACTIVE) ? POLLOUT
214*12Sigor@sysoev.ru                                                   : (POLLIN | POLLOUT);
215*12Sigor@sysoev.ru         nxt_eventport_enable_event(engine, ev, events);
216*12Sigor@sysoev.ru     }
217*12Sigor@sysoev.ru 
218*12Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
219*12Sigor@sysoev.ru }
220*12Sigor@sysoev.ru 
221*12Sigor@sysoev.ru 
222*12Sigor@sysoev.ru /*
223*12Sigor@sysoev.ru  * eventport changes are batched to improve instruction and data
224*12Sigor@sysoev.ru  * cache locality of several port_associate() and port_dissociate()
225*12Sigor@sysoev.ru  * calls followed by port_getn() call.
226*12Sigor@sysoev.ru  */
227*12Sigor@sysoev.ru 
228*12Sigor@sysoev.ru static void
229*12Sigor@sysoev.ru nxt_eventport_enable_event(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
230*12Sigor@sysoev.ru     nxt_uint_t events)
231*12Sigor@sysoev.ru {
232*12Sigor@sysoev.ru     nxt_eventport_change_t  *change;
233*12Sigor@sysoev.ru 
234*12Sigor@sysoev.ru     nxt_debug(ev->task, "port %d set event: fd:%d ev:%04XD u:%p",
235*12Sigor@sysoev.ru               engine->u.eventport.fd, ev->fd, events, ev);
236*12Sigor@sysoev.ru 
237*12Sigor@sysoev.ru     if (engine->u.eventport.nchanges >= engine->u.eventport.mchanges) {
238*12Sigor@sysoev.ru         (void) nxt_eventport_commit_changes(engine);
239*12Sigor@sysoev.ru     }
240*12Sigor@sysoev.ru 
241*12Sigor@sysoev.ru     ev->changing = 1;
242*12Sigor@sysoev.ru 
243*12Sigor@sysoev.ru     change = &engine->u.eventport.changes[engine->u.eventport.nchanges++];
244*12Sigor@sysoev.ru     change->events = events;
245*12Sigor@sysoev.ru     change->event = ev;
246*12Sigor@sysoev.ru }
247*12Sigor@sysoev.ru 
248*12Sigor@sysoev.ru 
249*12Sigor@sysoev.ru static void
250*12Sigor@sysoev.ru nxt_eventport_disable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
251*12Sigor@sysoev.ru {
252*12Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
253*12Sigor@sysoev.ru 
254*12Sigor@sysoev.ru     if (ev->write == NXT_EVENT_INACTIVE) {
255*12Sigor@sysoev.ru         nxt_eventport_disable_event(engine, ev);
256*12Sigor@sysoev.ru 
257*12Sigor@sysoev.ru     } else {
258*12Sigor@sysoev.ru         nxt_eventport_enable_event(engine, ev, POLLOUT);
259*12Sigor@sysoev.ru     }
260*12Sigor@sysoev.ru }
261*12Sigor@sysoev.ru 
262*12Sigor@sysoev.ru 
263*12Sigor@sysoev.ru static void
264*12Sigor@sysoev.ru nxt_eventport_disable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
265*12Sigor@sysoev.ru {
266*12Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
267*12Sigor@sysoev.ru 
268*12Sigor@sysoev.ru     if (ev->read == NXT_EVENT_INACTIVE) {
269*12Sigor@sysoev.ru         nxt_eventport_disable_event(engine, ev);
270*12Sigor@sysoev.ru 
271*12Sigor@sysoev.ru     } else {
272*12Sigor@sysoev.ru         nxt_eventport_enable_event(engine, ev, POLLIN);
273*12Sigor@sysoev.ru     }
274*12Sigor@sysoev.ru }
275*12Sigor@sysoev.ru 
276*12Sigor@sysoev.ru 
277*12Sigor@sysoev.ru static void
278*12Sigor@sysoev.ru nxt_eventport_disable_event(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
279*12Sigor@sysoev.ru {
280*12Sigor@sysoev.ru     nxt_eventport_change_t  *change;
281*12Sigor@sysoev.ru 
282*12Sigor@sysoev.ru     nxt_debug(ev->task, "port %d disable event : fd:%d",
283*12Sigor@sysoev.ru               engine->u.eventport.fd, ev->fd);
284*12Sigor@sysoev.ru 
285*12Sigor@sysoev.ru     if (engine->u.eventport.nchanges >= engine->u.eventport.mchanges) {
286*12Sigor@sysoev.ru         (void) nxt_eventport_commit_changes(engine);
287*12Sigor@sysoev.ru     }
288*12Sigor@sysoev.ru 
289*12Sigor@sysoev.ru     ev->changing = 1;
290*12Sigor@sysoev.ru 
291*12Sigor@sysoev.ru     change = &engine->u.eventport.changes[engine->u.eventport.nchanges++];
292*12Sigor@sysoev.ru     change->events = 0;
293*12Sigor@sysoev.ru     change->event = ev;
294*12Sigor@sysoev.ru }
295*12Sigor@sysoev.ru 
296*12Sigor@sysoev.ru 
297*12Sigor@sysoev.ru static nxt_int_t
298*12Sigor@sysoev.ru nxt_eventport_commit_changes(nxt_event_engine_t *engine)
299*12Sigor@sysoev.ru {
300*12Sigor@sysoev.ru     int                     ret, port;
301*12Sigor@sysoev.ru     nxt_int_t               retval;
302*12Sigor@sysoev.ru     nxt_fd_event_t          *ev;
303*12Sigor@sysoev.ru     nxt_eventport_change_t  *change, *end;
304*12Sigor@sysoev.ru 
305*12Sigor@sysoev.ru     port = engine->u.eventport.fd;
306*12Sigor@sysoev.ru 
307*12Sigor@sysoev.ru     nxt_debug(&engine->task, "eventport %d changes:%ui",
308*12Sigor@sysoev.ru               port, engine->u.eventport.nchanges);
309*12Sigor@sysoev.ru 
310*12Sigor@sysoev.ru     retval = NXT_OK;
311*12Sigor@sysoev.ru     change = engine->u.eventport.changes;
312*12Sigor@sysoev.ru     end = change + engine->u.eventport.nchanges;
313*12Sigor@sysoev.ru 
314*12Sigor@sysoev.ru     do {
315*12Sigor@sysoev.ru         ev = change->event;
316*12Sigor@sysoev.ru         ev->changing = 0;
317*12Sigor@sysoev.ru 
318*12Sigor@sysoev.ru         if (change->events != 0) {
319*12Sigor@sysoev.ru             nxt_debug(ev->task, "port_associate(%d): fd:%d ev:%04XD u:%p",
320*12Sigor@sysoev.ru                       port, ev->fd, change->events, ev);
321*12Sigor@sysoev.ru 
322*12Sigor@sysoev.ru             ret = port_associate(port, PORT_SOURCE_FD,
323*12Sigor@sysoev.ru                                  ev->fd, change->events, ev);
324*12Sigor@sysoev.ru 
325*12Sigor@sysoev.ru             if (nxt_fast_path(ret == 0)) {
326*12Sigor@sysoev.ru                 goto next;
327*12Sigor@sysoev.ru             }
328*12Sigor@sysoev.ru 
329*12Sigor@sysoev.ru             nxt_log(ev->task, NXT_LOG_CRIT,
330*12Sigor@sysoev.ru                     "port_associate(%d, %d, %d, %04XD) failed %E",
331*12Sigor@sysoev.ru                     port, PORT_SOURCE_FD, ev->fd, change->events, nxt_errno);
332*12Sigor@sysoev.ru 
333*12Sigor@sysoev.ru         } else {
334*12Sigor@sysoev.ru             nxt_debug(ev->task, "port_dissociate(%d): fd:%d", port, ev->fd);
335*12Sigor@sysoev.ru 
336*12Sigor@sysoev.ru             ret = port_dissociate(port, PORT_SOURCE_FD, ev->fd);
337*12Sigor@sysoev.ru 
338*12Sigor@sysoev.ru             if (nxt_fast_path(ret == 0)) {
339*12Sigor@sysoev.ru                 goto next;
340*12Sigor@sysoev.ru             }
341*12Sigor@sysoev.ru 
342*12Sigor@sysoev.ru             nxt_log(ev->task, NXT_LOG_CRIT,
343*12Sigor@sysoev.ru                     "port_dissociate(%d, %d, %d) failed %E",
344*12Sigor@sysoev.ru                     port, PORT_SOURCE_FD, ev->fd, nxt_errno);
345*12Sigor@sysoev.ru         }
346*12Sigor@sysoev.ru 
347*12Sigor@sysoev.ru         nxt_work_queue_add(&engine->fast_work_queue,
348*12Sigor@sysoev.ru                            nxt_eventport_error_handler,
349*12Sigor@sysoev.ru                            ev->task, ev, ev->data);
350*12Sigor@sysoev.ru 
351*12Sigor@sysoev.ru         retval = NXT_ERROR;
352*12Sigor@sysoev.ru 
353*12Sigor@sysoev.ru     next:
354*12Sigor@sysoev.ru 
355*12Sigor@sysoev.ru         change++;
356*12Sigor@sysoev.ru 
357*12Sigor@sysoev.ru     } while (change < end);
358*12Sigor@sysoev.ru 
359*12Sigor@sysoev.ru     engine->u.eventport.nchanges = 0;
360*12Sigor@sysoev.ru 
361*12Sigor@sysoev.ru     return retval;
362*12Sigor@sysoev.ru }
363*12Sigor@sysoev.ru 
364*12Sigor@sysoev.ru 
365*12Sigor@sysoev.ru static void
366*12Sigor@sysoev.ru nxt_eventport_error_handler(nxt_task_t *task, void *obj, void *data)
367*12Sigor@sysoev.ru {
368*12Sigor@sysoev.ru     nxt_fd_event_t  *ev;
369*12Sigor@sysoev.ru 
370*12Sigor@sysoev.ru     ev = obj;
371*12Sigor@sysoev.ru 
372*12Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
373*12Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
374*12Sigor@sysoev.ru 
375*12Sigor@sysoev.ru     ev->error_handler(task, ev, data);
376*12Sigor@sysoev.ru }
377*12Sigor@sysoev.ru 
378*12Sigor@sysoev.ru 
379*12Sigor@sysoev.ru static void
380*12Sigor@sysoev.ru nxt_eventport_block_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
381*12Sigor@sysoev.ru {
382*12Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
383*12Sigor@sysoev.ru         ev->read = NXT_EVENT_BLOCKED;
384*12Sigor@sysoev.ru     }
385*12Sigor@sysoev.ru }
386*12Sigor@sysoev.ru 
387*12Sigor@sysoev.ru 
388*12Sigor@sysoev.ru static void
389*12Sigor@sysoev.ru nxt_eventport_block_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
390*12Sigor@sysoev.ru {
391*12Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
392*12Sigor@sysoev.ru         ev->write = NXT_EVENT_BLOCKED;
393*12Sigor@sysoev.ru     }
394*12Sigor@sysoev.ru }
395*12Sigor@sysoev.ru 
396*12Sigor@sysoev.ru 
397*12Sigor@sysoev.ru static void
398*12Sigor@sysoev.ru nxt_eventport_oneshot_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
399*12Sigor@sysoev.ru {
400*12Sigor@sysoev.ru     if (ev->read == NXT_EVENT_INACTIVE) {
401*12Sigor@sysoev.ru         ev->read = NXT_EVENT_ACTIVE;
402*12Sigor@sysoev.ru 
403*12Sigor@sysoev.ru         nxt_eventport_enable_event(engine, ev, POLLIN);
404*12Sigor@sysoev.ru     }
405*12Sigor@sysoev.ru }
406*12Sigor@sysoev.ru 
407*12Sigor@sysoev.ru 
408*12Sigor@sysoev.ru static void
409*12Sigor@sysoev.ru nxt_eventport_oneshot_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
410*12Sigor@sysoev.ru {
411*12Sigor@sysoev.ru     if (ev->write == NXT_EVENT_INACTIVE) {
412*12Sigor@sysoev.ru         ev->write = NXT_EVENT_ACTIVE;
413*12Sigor@sysoev.ru 
414*12Sigor@sysoev.ru         nxt_eventport_enable_event(engine, ev, POLLOUT);
415*12Sigor@sysoev.ru     }
416*12Sigor@sysoev.ru }
417*12Sigor@sysoev.ru 
418*12Sigor@sysoev.ru 
419*12Sigor@sysoev.ru static void
420*12Sigor@sysoev.ru nxt_eventport_enable_accept(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
421*12Sigor@sysoev.ru {
422*12Sigor@sysoev.ru     ev->read = NXT_EVENT_LEVEL;
423*12Sigor@sysoev.ru 
424*12Sigor@sysoev.ru     nxt_eventport_enable_event(engine, ev, POLLIN);
425*12Sigor@sysoev.ru }
426*12Sigor@sysoev.ru 
427*12Sigor@sysoev.ru 
428*12Sigor@sysoev.ru static nxt_int_t
429*12Sigor@sysoev.ru nxt_eventport_enable_post(nxt_event_engine_t *engine,
430*12Sigor@sysoev.ru     nxt_work_handler_t handler)
431*12Sigor@sysoev.ru {
432*12Sigor@sysoev.ru     engine->u.eventport.post_handler = handler;
433*12Sigor@sysoev.ru 
434*12Sigor@sysoev.ru     return NXT_OK;
435*12Sigor@sysoev.ru }
436*12Sigor@sysoev.ru 
437*12Sigor@sysoev.ru 
438*12Sigor@sysoev.ru static void
439*12Sigor@sysoev.ru nxt_eventport_signal(nxt_event_engine_t *engine, nxt_uint_t signo)
440*12Sigor@sysoev.ru {
441*12Sigor@sysoev.ru     int  port;
442*12Sigor@sysoev.ru 
443*12Sigor@sysoev.ru     port = engine->u.eventport.fd;
444*12Sigor@sysoev.ru 
445*12Sigor@sysoev.ru     nxt_debug(&engine->task, "port_send(%d, %ui)", port, signo);
446*12Sigor@sysoev.ru 
447*12Sigor@sysoev.ru     if (port_send(port, signo, NULL) != 0) {
448*12Sigor@sysoev.ru         nxt_log(&engine->task, NXT_LOG_CRIT, "port_send(%d) failed %E",
449*12Sigor@sysoev.ru                 port, nxt_errno);
450*12Sigor@sysoev.ru     }
451*12Sigor@sysoev.ru }
452*12Sigor@sysoev.ru 
453*12Sigor@sysoev.ru 
454*12Sigor@sysoev.ru static void
455*12Sigor@sysoev.ru nxt_eventport_poll(nxt_event_engine_t *engine, nxt_msec_t timeout)
456*12Sigor@sysoev.ru {
457*12Sigor@sysoev.ru     int                 n, events, signo;
458*12Sigor@sysoev.ru     uint_t              nevents;
459*12Sigor@sysoev.ru     nxt_err_t           err;
460*12Sigor@sysoev.ru     nxt_uint_t          i, level;
461*12Sigor@sysoev.ru     timespec_t          ts, *tp;
462*12Sigor@sysoev.ru     port_event_t        *event;
463*12Sigor@sysoev.ru     nxt_fd_event_t      *ev;
464*12Sigor@sysoev.ru     nxt_work_handler_t  handler;
465*12Sigor@sysoev.ru 
466*12Sigor@sysoev.ru     if (engine->u.eventport.nchanges != 0) {
467*12Sigor@sysoev.ru         if (nxt_eventport_commit_changes(engine) != NXT_OK) {
468*12Sigor@sysoev.ru             /* Error handlers have been enqueued on failure. */
469*12Sigor@sysoev.ru             timeout = 0;
470*12Sigor@sysoev.ru         }
471*12Sigor@sysoev.ru     }
472*12Sigor@sysoev.ru 
473*12Sigor@sysoev.ru     if (timeout == NXT_INFINITE_MSEC) {
474*12Sigor@sysoev.ru         tp = NULL;
475*12Sigor@sysoev.ru 
476*12Sigor@sysoev.ru     } else {
477*12Sigor@sysoev.ru         ts.tv_sec = timeout / 1000;
478*12Sigor@sysoev.ru         ts.tv_nsec = (timeout % 1000) * 1000000;
479*12Sigor@sysoev.ru         tp = &ts;
480*12Sigor@sysoev.ru     }
481*12Sigor@sysoev.ru 
482*12Sigor@sysoev.ru     nxt_debug(&engine->task, "port_getn(%d) timeout: %M",
483*12Sigor@sysoev.ru               engine->u.eventport.fd, timeout);
484*12Sigor@sysoev.ru 
485*12Sigor@sysoev.ru     /*
486*12Sigor@sysoev.ru      * A trap for possible error when Solaris does not update nevents
487*12Sigor@sysoev.ru      * if ETIME or EINTR is returned.  This issue will be logged as
488*12Sigor@sysoev.ru      * "unexpected port_getn() event".
489*12Sigor@sysoev.ru      *
490*12Sigor@sysoev.ru      * The details are in OpenSolaris mailing list thread "port_getn()
491*12Sigor@sysoev.ru      * and timeouts - is this a bug or an undocumented feature?"
492*12Sigor@sysoev.ru      */
493*12Sigor@sysoev.ru     event = &engine->u.eventport.events[0];
494*12Sigor@sysoev.ru     event->portev_events = -1; /* invalid port events */
495*12Sigor@sysoev.ru     event->portev_source = -1; /* invalid port source */
496*12Sigor@sysoev.ru     event->portev_object = -1;
497*12Sigor@sysoev.ru     event->portev_user = (void *) -1;
498*12Sigor@sysoev.ru 
499*12Sigor@sysoev.ru     nevents = 1;
500*12Sigor@sysoev.ru     n = port_getn(engine->u.eventport.fd, engine->u.eventport.events,
501*12Sigor@sysoev.ru                   engine->u.eventport.mevents, &nevents, tp);
502*12Sigor@sysoev.ru 
503*12Sigor@sysoev.ru     /*
504*12Sigor@sysoev.ru      * 32-bit port_getn() on Solaris 10 x86 returns large negative
505*12Sigor@sysoev.ru      * values instead of 0 when returning immediately.
506*12Sigor@sysoev.ru      */
507*12Sigor@sysoev.ru     err = (n < 0) ? nxt_errno : 0;
508*12Sigor@sysoev.ru 
509*12Sigor@sysoev.ru     nxt_thread_time_update(engine->task.thread);
510*12Sigor@sysoev.ru 
511*12Sigor@sysoev.ru     if (n == -1) {
512*12Sigor@sysoev.ru         if (err == NXT_ETIME || err == NXT_EINTR) {
513*12Sigor@sysoev.ru             if (nevents != 0) {
514*12Sigor@sysoev.ru                 nxt_log(&engine->task, NXT_LOG_CRIT,
515*12Sigor@sysoev.ru                         "port_getn(%d) failed %E, events:%ud",
516*12Sigor@sysoev.ru                         engine->u.eventport.fd, err, nevents);
517*12Sigor@sysoev.ru             }
518*12Sigor@sysoev.ru         }
519*12Sigor@sysoev.ru 
520*12Sigor@sysoev.ru         if (err != NXT_ETIME) {
521*12Sigor@sysoev.ru             level = (err == NXT_EINTR) ? NXT_LOG_INFO : NXT_LOG_CRIT;
522*12Sigor@sysoev.ru 
523*12Sigor@sysoev.ru             nxt_log(&engine->task, level, "port_getn(%d) failed %E",
524*12Sigor@sysoev.ru                     engine->u.eventport.fd, err);
525*12Sigor@sysoev.ru 
526*12Sigor@sysoev.ru             if (err != NXT_EINTR) {
527*12Sigor@sysoev.ru                 return;
528*12Sigor@sysoev.ru             }
529*12Sigor@sysoev.ru         }
530*12Sigor@sysoev.ru     }
531*12Sigor@sysoev.ru 
532*12Sigor@sysoev.ru     nxt_debug(&engine->task, "port_getn(%d) events: %d",
533*12Sigor@sysoev.ru               engine->u.eventport.fd, nevents);
534*12Sigor@sysoev.ru 
535*12Sigor@sysoev.ru     for (i = 0; i < nevents; i++) {
536*12Sigor@sysoev.ru         event = &engine->u.eventport.events[i];
537*12Sigor@sysoev.ru 
538*12Sigor@sysoev.ru         switch (event->portev_source) {
539*12Sigor@sysoev.ru 
540*12Sigor@sysoev.ru         case PORT_SOURCE_FD:
541*12Sigor@sysoev.ru             ev = event->portev_user;
542*12Sigor@sysoev.ru             events = event->portev_events;
543*12Sigor@sysoev.ru 
544*12Sigor@sysoev.ru             nxt_debug(ev->task, "eventport: fd:%d ev:%04Xd u:%p rd:%d wr:%d",
545*12Sigor@sysoev.ru                       event->portev_object, events, ev, ev->read, ev->write);
546*12Sigor@sysoev.ru 
547*12Sigor@sysoev.ru             if (nxt_slow_path(events & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
548*12Sigor@sysoev.ru                 nxt_log(ev->task, NXT_LOG_CRIT,
549*12Sigor@sysoev.ru                         "port_getn(%d) error fd:%d events:%04Xud",
550*12Sigor@sysoev.ru                         engine->u.eventport.fd, ev->fd, events);
551*12Sigor@sysoev.ru 
552*12Sigor@sysoev.ru                 nxt_work_queue_add(&engine->fast_work_queue,
553*12Sigor@sysoev.ru                                    nxt_eventport_error_handler,
554*12Sigor@sysoev.ru                                    ev->task, ev, ev->data);
555*12Sigor@sysoev.ru                 continue;
556*12Sigor@sysoev.ru             }
557*12Sigor@sysoev.ru 
558*12Sigor@sysoev.ru             if (events & POLLIN) {
559*12Sigor@sysoev.ru                 ev->read_ready = 1;
560*12Sigor@sysoev.ru 
561*12Sigor@sysoev.ru                 if (ev->read != NXT_EVENT_BLOCKED) {
562*12Sigor@sysoev.ru                     nxt_work_queue_add(ev->read_work_queue, ev->read_handler,
563*12Sigor@sysoev.ru                                        ev->task, ev, ev->data);
564*12Sigor@sysoev.ru 
565*12Sigor@sysoev.ru                 }
566*12Sigor@sysoev.ru 
567*12Sigor@sysoev.ru                 if (ev->read != NXT_EVENT_LEVEL) {
568*12Sigor@sysoev.ru                     ev->read = NXT_EVENT_INACTIVE;
569*12Sigor@sysoev.ru                 }
570*12Sigor@sysoev.ru             }
571*12Sigor@sysoev.ru 
572*12Sigor@sysoev.ru             if (events & POLLOUT) {
573*12Sigor@sysoev.ru                 ev->write_ready = 1;
574*12Sigor@sysoev.ru 
575*12Sigor@sysoev.ru                 if (ev->write != NXT_EVENT_BLOCKED) {
576*12Sigor@sysoev.ru                     nxt_work_queue_add(ev->write_work_queue, ev->write_handler,
577*12Sigor@sysoev.ru                                        ev->task, ev, ev->data);
578*12Sigor@sysoev.ru                 }
579*12Sigor@sysoev.ru 
580*12Sigor@sysoev.ru                 ev->write = NXT_EVENT_INACTIVE;
581*12Sigor@sysoev.ru             }
582*12Sigor@sysoev.ru 
583*12Sigor@sysoev.ru             /*
584*12Sigor@sysoev.ru              * Reactivate counterpart direction, because the
585*12Sigor@sysoev.ru              * eventport is oneshot notification facility.
586*12Sigor@sysoev.ru              */
587*12Sigor@sysoev.ru             events = (ev->read == NXT_EVENT_INACTIVE) ? 0 : POLLIN;
588*12Sigor@sysoev.ru             events |= (ev->write == NXT_EVENT_INACTIVE) ? 0 : POLLOUT;
589*12Sigor@sysoev.ru 
590*12Sigor@sysoev.ru             if (events != 0) {
591*12Sigor@sysoev.ru                 nxt_eventport_enable_event(engine, ev, events);
592*12Sigor@sysoev.ru             }
593*12Sigor@sysoev.ru 
594*12Sigor@sysoev.ru             break;
595*12Sigor@sysoev.ru 
596*12Sigor@sysoev.ru         case PORT_SOURCE_USER:
597*12Sigor@sysoev.ru             nxt_debug(&engine->task, "eventport: user ev:%d u:%p",
598*12Sigor@sysoev.ru                       event->portev_events, event->portev_user);
599*12Sigor@sysoev.ru 
600*12Sigor@sysoev.ru             signo = event->portev_events;
601*12Sigor@sysoev.ru 
602*12Sigor@sysoev.ru             handler = (signo == 0) ? engine->u.eventport.post_handler
603*12Sigor@sysoev.ru                                    : engine->u.eventport.signal_handler;
604*12Sigor@sysoev.ru 
605*12Sigor@sysoev.ru             nxt_work_queue_add(&engine->fast_work_queue, handler,
606*12Sigor@sysoev.ru                                &engine->task, (void *) (uintptr_t) signo, NULL);
607*12Sigor@sysoev.ru 
608*12Sigor@sysoev.ru             break;
609*12Sigor@sysoev.ru 
610*12Sigor@sysoev.ru         default:
611*12Sigor@sysoev.ru             nxt_log(&engine->task, NXT_LOG_CRIT,
612*12Sigor@sysoev.ru                     "unexpected port_getn(%d) event: ev:%d src:%d obj:%p u:%p",
613*12Sigor@sysoev.ru                     engine->u.eventport.fd, event->portev_events,
614*12Sigor@sysoev.ru                     event->portev_source, event->portev_object,
615*12Sigor@sysoev.ru                     event->portev_user);
616*12Sigor@sysoev.ru         }
617*12Sigor@sysoev.ru     }
618*12Sigor@sysoev.ru }
619