1 /* Copyright libuv project contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include "uv.h"
23 #include "task.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #if !defined(__linux__) && !defined(__FreeBSD__) && \
30 !defined(__DragonFly__) && !defined(__sun) && !defined(_AIX73)
31
TEST_IMPL(tcp_reuseport)32 TEST_IMPL(tcp_reuseport) {
33 struct sockaddr_in addr;
34 uv_loop_t* loop;
35 uv_tcp_t handle;
36 int r;
37
38 ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
39
40 loop = uv_default_loop();
41 ASSERT_NOT_NULL(loop);
42
43 r = uv_tcp_init(loop, &handle);
44 ASSERT_OK(r);
45
46 r = uv_tcp_bind(&handle, (const struct sockaddr*) &addr, UV_TCP_REUSEPORT);
47 ASSERT_EQ(r, UV_ENOTSUP);
48
49 MAKE_VALGRIND_HAPPY(loop);
50
51 return 0;
52 }
53
54 #else
55
56 #define NUM_LISTENING_THREADS 2
57 #define MAX_TCP_CLIENTS 10
58
59 static uv_tcp_t tcp_connect_handles[MAX_TCP_CLIENTS];
60 static uv_connect_t tcp_connect_requests[MAX_TCP_CLIENTS];
61
62 static uv_sem_t semaphore;
63
64 static uv_mutex_t mutex;
65 static unsigned int accepted;
66
67 static unsigned int thread_loop1_accepted;
68 static unsigned int thread_loop2_accepted;
69 static unsigned int connected;
70
71 static uv_loop_t* main_loop;
72 static uv_loop_t thread_loop1;
73 static uv_loop_t thread_loop2;
74 static uv_tcp_t thread_handle1;
75 static uv_tcp_t thread_handle2;
76 static uv_timer_t thread_timer_handle1;
77 static uv_timer_t thread_timer_handle2;
78
on_close(uv_handle_t * handle)79 static void on_close(uv_handle_t* handle) {
80 free(handle);
81 }
82
ticktack(uv_timer_t * timer)83 static void ticktack(uv_timer_t* timer) {
84 ASSERT(timer == &thread_timer_handle1 || timer == &thread_timer_handle2);
85
86 int done = 0;
87 uv_mutex_lock(&mutex);
88 if (accepted == MAX_TCP_CLIENTS) {
89 done = 1;
90 }
91 uv_mutex_unlock(&mutex);
92
93 if (done) {
94 uv_close((uv_handle_t*) timer, NULL);
95 if (timer->loop == &thread_loop1)
96 uv_close((uv_handle_t*) &thread_handle1, NULL);
97 if (timer->loop == &thread_loop2)
98 uv_close((uv_handle_t*) &thread_handle2, NULL);
99 }
100 }
101
on_connection(uv_stream_t * server,int status)102 static void on_connection(uv_stream_t* server, int status)
103 {
104 ASSERT_OK(status);
105 ASSERT(server == (uv_stream_t*) &thread_handle1 || \
106 server == (uv_stream_t*) &thread_handle2);
107
108 uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
109 ASSERT_OK(uv_tcp_init(server->loop, client));
110 ASSERT_OK(uv_accept(server, (uv_stream_t*) client));
111 uv_close((uv_handle_t*) client, on_close);
112
113 if (server->loop == &thread_loop1)
114 thread_loop1_accepted++;
115
116 if (server->loop == &thread_loop2)
117 thread_loop2_accepted++;
118
119 uv_mutex_lock(&mutex);
120 accepted++;
121 uv_mutex_unlock(&mutex);
122 }
123
on_connect(uv_connect_t * req,int status)124 static void on_connect(uv_connect_t* req, int status) {
125 ASSERT_OK(status);
126 ASSERT_NOT_NULL(req->handle);
127 ASSERT_PTR_EQ(req->handle->loop, main_loop);
128
129 connected++;
130 uv_close((uv_handle_t*) req->handle, NULL);
131 }
132
create_listener(uv_loop_t * loop,uv_tcp_t * handle)133 static void create_listener(uv_loop_t* loop, uv_tcp_t* handle) {
134 struct sockaddr_in addr;
135 int r;
136
137 ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
138
139 r = uv_tcp_init(loop, handle);
140 ASSERT_OK(r);
141
142 r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, UV_TCP_REUSEPORT);
143 ASSERT_OK(r);
144
145 r = uv_listen((uv_stream_t*) handle, 128, on_connection);
146 ASSERT_OK(r);
147 }
148
run_event_loop(void * arg)149 static void run_event_loop(void* arg) {
150 int r;
151 uv_tcp_t* handle;
152 uv_timer_t* timer;
153 uv_loop_t* loop = (uv_loop_t*) arg;
154 ASSERT(loop == &thread_loop1 || loop == &thread_loop2);
155
156 if (loop == &thread_loop1) {
157 handle = &thread_handle1;
158 timer = &thread_timer_handle1;
159 } else {
160 handle = &thread_handle2;
161 timer = &thread_timer_handle2;
162 }
163
164 create_listener(loop, handle);
165 r = uv_timer_init(loop, timer);
166 ASSERT_OK(r);
167 r = uv_timer_start(timer, ticktack, 0, 10);
168 ASSERT_OK(r);
169
170 /* Notify the main thread to start connecting. */
171 uv_sem_post(&semaphore);
172 r = uv_run(loop, UV_RUN_DEFAULT);
173 ASSERT_OK(r);
174 }
175
TEST_IMPL(tcp_reuseport)176 TEST_IMPL(tcp_reuseport) {
177 struct sockaddr_in addr;
178 int r;
179 int i;
180
181 r = uv_mutex_init(&mutex);
182 ASSERT_OK(r);
183
184 r = uv_sem_init(&semaphore, 0);
185 ASSERT_OK(r);
186
187 main_loop = uv_default_loop();
188 ASSERT_NOT_NULL(main_loop);
189
190 /* Run event loops of listeners in separate threads. */
191 uv_loop_init(&thread_loop1);
192 uv_loop_init(&thread_loop2);
193 uv_thread_t thread_loop_id1;
194 uv_thread_t thread_loop_id2;
195 uv_thread_create(&thread_loop_id1, run_event_loop, &thread_loop1);
196 uv_thread_create(&thread_loop_id2, run_event_loop, &thread_loop2);
197
198 /* Wait until all threads to poll for accepting connections
199 * before we start to connect. Otherwise the incoming connections
200 * might not be distributed across all listening threads. */
201 for (i = 0; i < NUM_LISTENING_THREADS; i++)
202 uv_sem_wait(&semaphore);
203 /* Now we know all threads are up and entering the uv_run(),
204 * but we still sleep a little bit just for dual fail-safe. */
205 uv_sleep(100);
206
207 /* Start connecting to the peers. */
208 ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
209 for (i = 0; i < MAX_TCP_CLIENTS; i++) {
210 r = uv_tcp_init(main_loop, &tcp_connect_handles[i]);
211 ASSERT_OK(r);
212 r = uv_tcp_connect(&tcp_connect_requests[i],
213 &tcp_connect_handles[i],
214 (const struct sockaddr*) &addr,
215 on_connect);
216 ASSERT_OK(r);
217 }
218
219 r = uv_run(main_loop, UV_RUN_DEFAULT);
220 ASSERT_OK(r);
221
222 /* Wait for all threads to exit. */
223 uv_thread_join(&thread_loop_id1);
224 uv_thread_join(&thread_loop_id2);
225
226 /* Verify if each listener per event loop accepted connections
227 * and the amount of accepted connections matches the one of
228 * connected connections.
229 */
230 ASSERT_EQ(accepted, MAX_TCP_CLIENTS);
231 ASSERT_EQ(connected, MAX_TCP_CLIENTS);
232 ASSERT_GT(thread_loop1_accepted, 0);
233 ASSERT_GT(thread_loop2_accepted, 0);
234 ASSERT_EQ(thread_loop1_accepted + thread_loop2_accepted, connected);
235
236 /* Clean up. */
237 uv_mutex_destroy(&mutex);
238
239 uv_sem_destroy(&semaphore);
240
241 uv_loop_close(&thread_loop1);
242 uv_loop_close(&thread_loop2);
243 MAKE_VALGRIND_HAPPY(main_loop);
244
245 return 0;
246 }
247
248 #endif
249