1 /*
2  * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>.
3  *
4  * Permission to use, copy, modify, and/or 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  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
12  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
14  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
16  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
17  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
18  * POSSIBILITY OF SUCH DAMAGE.
19  */
20 
21 #include "mowgli.h"
22 
23 #ifdef HAVE_PORT_CREATE
24 
25 # include <port.h>
26 
27 typedef struct
28 {
29 	int port_fd;
30 	int pfd_size;
31 	port_event_t *pfd;
32 } mowgli_ports_eventloop_private_t;
33 
34 static void
mowgli_ports_eventloop_pollsetup(mowgli_eventloop_t * eventloop)35 mowgli_ports_eventloop_pollsetup(mowgli_eventloop_t *eventloop)
36 {
37 	mowgli_ports_eventloop_private_t *priv;
38 
39 	priv = mowgli_alloc(sizeof(mowgli_ports_eventloop_private_t));
40 	eventloop->poller = priv;
41 
42 	priv->pfd_size = getdtablesize();
43 	priv->port_fd = port_create();
44 	priv->pfd = mowgli_alloc(sizeof(port_event_t) * priv->pfd_size);
45 
46 	return;
47 }
48 
49 static void
mowgli_ports_eventloop_pollshutdown(mowgli_eventloop_t * eventloop)50 mowgli_ports_eventloop_pollshutdown(mowgli_eventloop_t *eventloop)
51 {
52 	mowgli_ports_eventloop_private_t *priv;
53 
54 	return_if_fail(eventloop != NULL);
55 
56 	priv = eventloop->poller;
57 
58 	close(priv->port_fd);
59 
60 	mowgli_free(priv->pfd);
61 	mowgli_free(priv);
62 	return;
63 }
64 
65 static void
mowgli_ports_eventloop_destroy(mowgli_eventloop_t * eventloop,mowgli_eventloop_pollable_t * pollable)66 mowgli_ports_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable)
67 {
68 	mowgli_ports_eventloop_private_t *priv;
69 
70 	return_if_fail(eventloop != NULL);
71 	return_if_fail(pollable != NULL);
72 
73 	priv = eventloop->poller;
74 	pollable->slot = 0;
75 
76 	if (port_dissociate(priv->port_fd, PORT_SOURCE_FD, (uintptr_t) pollable->fd) < 0)
77 	{
78 		if (mowgli_eventloop_ignore_errno(errno))
79 			return;
80 
81 		mowgli_log("mowgli_ports_eventloop_destroy(): port_dissociate failed: %d (%s)", errno, strerror(errno));
82 	}
83 }
84 
85 static void
mowgli_ports_eventloop_setselect(mowgli_eventloop_t * eventloop,mowgli_eventloop_pollable_t * pollable,mowgli_eventloop_io_dir_t dir,mowgli_eventloop_io_cb_t * event_function)86 mowgli_ports_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function)
87 {
88 	mowgli_ports_eventloop_private_t *priv;
89 	unsigned int old_flags;
90 
91 	return_if_fail(eventloop != NULL);
92 	return_if_fail(pollable != NULL);
93 
94 	priv = eventloop->poller;
95 	old_flags = pollable->slot;
96 
97 # ifdef DEBUG
98 	mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function);
99 # endif
100 
101 	switch (dir)
102 	{
103 	case MOWGLI_EVENTLOOP_IO_READ:
104 		pollable->read_function = event_function;
105 		pollable->slot |= POLLIN;
106 		break;
107 	case MOWGLI_EVENTLOOP_IO_WRITE:
108 		pollable->write_function = event_function;
109 		pollable->slot |= POLLOUT;
110 		break;
111 	default:
112 		mowgli_log("unhandled pollable direction %d", dir);
113 		break;
114 	}
115 
116 # ifdef DEBUG
117 	mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function);
118 # endif
119 
120 	if (pollable->read_function == NULL)
121 		pollable->slot &= ~POLLIN;
122 
123 	if (pollable->write_function == NULL)
124 		pollable->slot &= ~POLLOUT;
125 
126 	if ((old_flags == 0) && (pollable->slot == 0))
127 	{
128 		return;
129 	}
130 	else if (pollable->slot == 0)
131 	{
132 		port_dissociate(priv->port_fd, PORT_SOURCE_FD, (uintptr_t) pollable->fd);
133 		return;
134 	}
135 
136 	if (port_associate(priv->port_fd, PORT_SOURCE_FD, (uintptr_t) pollable->fd, pollable->slot, pollable) < 0)
137 	{
138 		if (mowgli_eventloop_ignore_errno(errno))
139 			return;
140 
141 		mowgli_log("mowgli_ports_eventloop_setselect(): port_associate failed: %d (%s)", errno, strerror(errno));
142 	}
143 
144 	return;
145 }
146 
147 static void
mowgli_ports_eventloop_select(mowgli_eventloop_t * eventloop,int delay)148 mowgli_ports_eventloop_select(mowgli_eventloop_t *eventloop, int delay)
149 {
150 	mowgli_ports_eventloop_private_t *priv;
151 	int i, ret, o_errno, nget = 1;
152 
153 	return_if_fail(eventloop != NULL);
154 
155 	priv = eventloop->poller;
156 
157 	ret = port_getn(priv->port_fd, priv->pfd, priv->pfd_size, &nget,
158 			delay >= 0 ? &(struct timespec) { .tv_sec = delay / 1000, .tv_nsec = delay % 1000 * 1000000 } : NULL);
159 
160 	o_errno = errno;
161 	mowgli_eventloop_synchronize(eventloop);
162 
163 	if (ret == -1)
164 	{
165 		if (mowgli_eventloop_ignore_errno(o_errno))
166 			return;
167 
168 		mowgli_log("mowgli_ports_eventloop_select(): port_getn failed: %d (%s)", o_errno, strerror(o_errno));
169 		return;
170 	}
171 
172 	for (i = 0; i < nget; i++)
173 	{
174 		mowgli_eventloop_pollable_t *pollable = priv->pfd[i].portev_user;
175 
176 		if (priv->pfd[i].portev_source != PORT_SOURCE_FD)
177 			continue;
178 
179 		if (priv->pfd[i].portev_events & (POLLIN | POLLHUP | POLLERR))
180 			mowgli_pollable_trigger(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ);
181 
182 		if (priv->pfd[i].portev_events & (POLLOUT | POLLHUP | POLLERR))
183 			mowgli_pollable_trigger(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE);
184 	}
185 }
186 
187 mowgli_eventloop_ops_t _mowgli_ports_pollops =
188 {
189 	.timeout_once = mowgli_simple_eventloop_timeout_once,
190 	.run_once = mowgli_simple_eventloop_run_once,
191 	.pollsetup = mowgli_ports_eventloop_pollsetup,
192 	.pollshutdown = mowgli_ports_eventloop_pollshutdown,
193 	.setselect = mowgli_ports_eventloop_setselect,
194 	.select = mowgli_ports_eventloop_select,
195 	.destroy = mowgli_ports_eventloop_destroy,
196 };
197 
198 #endif
199