1/*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <stdlib.h>
18#include <port.h>
19#include <poll.h>
20
21#include "sys/event.h"
22#include "private.h"
23
24const struct filter evfilt_vnode = EVFILT_NOTIMPL;
25const struct filter evfilt_proc  = EVFILT_NOTIMPL;
26
27/* Dump a poll(2) events bitmask */
28static char *
29poll_events_dump(short events)
30{
31    static char __thread buf[512];
32
33#define _PL_DUMP(attrib) \
34    if (events == attrib) \
35       strcat(&buf[0], " "#attrib);
36
37    snprintf(&buf[0], 512, "events = %hd 0x%o (", events, events);
38    _PL_DUMP(POLLIN);
39    _PL_DUMP(POLLPRI);
40    _PL_DUMP(POLLOUT);
41    _PL_DUMP(POLLRDNORM);
42    _PL_DUMP(POLLRDBAND);
43    _PL_DUMP(POLLWRBAND);
44    _PL_DUMP(POLLERR);
45    _PL_DUMP(POLLHUP);
46    _PL_DUMP(POLLNVAL);
47    strcat(&buf[0], ")");
48
49    return (&buf[0]);
50
51#undef _PL_DUMP
52}
53
54static char *
55port_event_dump(port_event_t *evt)
56{
57    static char __thread buf[512];
58
59    if (evt == NULL)
60        return "(null)";
61
62#define PE_DUMP(attrib) \
63    if (evt->portev_source == attrib) \
64       strcat(&buf[0], #attrib);
65
66    snprintf(&buf[0], 512,
67                " { object = %u, user = %p, %s, source = %d (",
68                (unsigned int) evt->portev_object,
69                evt->portev_user,
70                poll_events_dump(evt->portev_events),
71                evt->portev_source);
72    PE_DUMP(PORT_SOURCE_AIO);
73    PE_DUMP(PORT_SOURCE_FD);
74    PE_DUMP(PORT_SOURCE_TIMER);
75    PE_DUMP(PORT_SOURCE_USER);
76    PE_DUMP(PORT_SOURCE_ALERT);
77    strcat(&buf[0], ") }\n");
78
79    return (&buf[0]);
80#undef PE_DUMP
81}
82
83int
84kevent_wait(struct kqueue *kq, const struct timespec *timeout)
85{
86    port_event_t *pe = (port_event_t *) pthread_getspecific(kq->kq_port_event);
87
88    int rv;
89    uint_t nget = 1;
90
91    reset_errno();
92    dbg_printf("waiting for events (timeout=%p)", timeout);
93    rv = port_getn(kq->kq_port, pe, 1, &nget, (struct timespec *) timeout);
94    dbg_printf("rv=%d errno=%d (%s) nget=%d",
95                rv, errno, strerror(errno), nget);
96    if (rv < 0) {
97        if (errno == ETIME) {
98            dbg_puts("no events within the given timeout");
99            return (0);
100        }
101        if (errno == EINTR) {
102            dbg_puts("signal caught");
103            return (-1);
104        }
105        dbg_perror("port_get(2)");
106        return (-1);
107    }
108
109    return (nget);
110}
111
112int
113kevent_copyout(struct kqueue *kq, int nready,
114        struct kevent *eventlist, int nevents)
115{
116    port_event_t *pe = (port_event_t *) pthread_getspecific(kq->kq_port_event);
117    struct filter *filt;
118    int rv;
119
120    dbg_printf("%s", port_event_dump(pe));
121    switch (pe->portev_source) {
122	case PORT_SOURCE_FD:
123        filt = pe->portev_user;
124        rv = filt->kf_copyout(filt, eventlist, nevents);
125        break;
126
127	case PORT_SOURCE_TIMER:
128        filter_lookup(&filt, kq, EVFILT_TIMER);
129        rv = filt->kf_copyout(filt, eventlist, nevents);
130        break;
131
132	case PORT_SOURCE_USER:
133        switch (pe->portev_events) {
134            case X_PORT_SOURCE_SIGNAL:
135                filter_lookup(&filt, kq, EVFILT_SIGNAL);
136                rv = filt->kf_copyout(filt, eventlist, nevents);
137                break;
138            case X_PORT_SOURCE_USER:
139                filter_lookup(&filt, kq, EVFILT_USER);
140                rv = filt->kf_copyout(filt, eventlist, nevents);
141                break;
142            default:
143                dbg_puts("unsupported portev_events");
144                abort();
145        }
146        break;
147
148	default:
149		dbg_puts("unsupported source");
150    		abort();
151    }
152    if (rv < 0) {
153        dbg_puts("kevent_copyout failed");
154	return (-1);
155    }
156
157    return (1);
158}
159