1 /*
2  * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #if defined(__ZEPHYR__) && !(defined(CONFIG_BOARD_NATIVE_POSIX_32BIT) \
7 	|| defined(CONFIG_BOARD_NATIVE_POSIX_64BIT) \
8 	|| defined(CONFIG_SOC_SERIES_BSIM_NRFXX))
9 
10 #include <net/socket.h>
11 #include <posix/pthread.h>
12 #include <sys/util.h>
13 #include <posix/unistd.h>
14 
15 #else
16 
17 #include <poll.h>
18 #include <pthread.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
24 
25 #endif
26 
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 #define NUM_SOCKETPAIRS 3
33 #define NUM_REPITITIONS 3
34 
35 struct ctx {
36 	int spair[2];
37 	pthread_t thread;
38 	char *name;
39 };
40 
41 static const char *const names[] = {
42 	"Alpha",
43 	"Bravo",
44 	"Charlie",
45 };
46 
47 #if defined(__ZEPHYR__) && !(defined(CONFIG_BOARD_NATIVE_POSIX_32BIT) \
48 	|| defined(CONFIG_BOARD_NATIVE_POSIX_64BIT) \
49 	|| defined(CONFIG_SOC_SERIES_BSIM_NRFXX))
50 
51 #define STACK_SIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
52 static pthread_attr_t attr[NUM_SOCKETPAIRS];
53 K_THREAD_STACK_ARRAY_DEFINE(stack, NUM_SOCKETPAIRS, STACK_SIZE);
54 
55 #endif
56 
hello(int fd,const char * name)57 static void hello(int fd, const char *name)
58 {
59 	/* write(2) should be used after #25443 */
60 	int res = send(fd, name, strlen(name), 0);
61 
62 	if (res != strlen(name)) {
63 		printf("%s(): send: expected: %d actual: %d errno: %d\n",
64 			__func__, (int)strlen(name), res, errno);
65 	}
66 }
67 
fun(void * arg)68 static void *fun(void *arg)
69 {
70 	struct ctx *const ctx = (struct ctx *)arg;
71 	int fd = ctx->spair[1];
72 	const char *name = ctx->name;
73 
74 	for (size_t i = 0; i < NUM_REPITITIONS; ++i) {
75 		hello(fd, name);
76 	}
77 
78 	close(fd);
79 	printf("%s closed fd %d\n", name, fd);
80 	ctx->spair[1] = -1;
81 
82 	return NULL;
83 }
84 
fd_to_idx(int fd,struct ctx * ctx,size_t n)85 static int fd_to_idx(int fd, struct ctx *ctx, size_t n)
86 {
87 	int r = -1;
88 	size_t i;
89 
90 	for (i = 0; i < n; ++i) {
91 		if (ctx[i].spair[0] == fd) {
92 			r = i;
93 			break;
94 		}
95 	}
96 
97 	return r;
98 }
99 
100 #ifdef __ZEPHYR__
zephyr_app_main(void)101 void zephyr_app_main(void)
102 {
103 #else
104 int main(int argc, char *argv[])
105 {
106 	(void) argc;
107 	(void) argv;
108 #endif
109 
110 	int r;
111 	int fd;
112 	int idx;
113 	int poll_r;
114 	size_t i;
115 	size_t num_active;
116 	char buf[32];
117 	struct ctx ctx[NUM_SOCKETPAIRS] = {};
118 	struct pollfd fds[NUM_SOCKETPAIRS] = {};
119 	void *unused;
120 	pthread_attr_t *attrp = NULL;
121 
122 	for (i = 0; i < ARRAY_SIZE(ctx); ++i) {
123 		ctx[i].name = (char *)names[i];
124 		r = socketpair(AF_UNIX, SOCK_STREAM, 0, ctx[i].spair);
125 		if (r != 0) {
126 			printf("socketpair failed: %d\n", errno);
127 			goto out;
128 		}
129 
130 #if defined(__ZEPHYR__) && !(defined(CONFIG_BOARD_NATIVE_POSIX_32BIT) \
131 	|| defined(CONFIG_BOARD_NATIVE_POSIX_64BIT) \
132 	|| defined(CONFIG_SOC_SERIES_BSIM_NRFXX))
133 		/* Zephyr requires a non-NULL attribute for pthread_create */
134 		attrp = &attr[i];
135 		r = pthread_attr_init(attrp);
136 		if (r != 0) {
137 			printf("pthread_attr_init() failed: %d", r);
138 			goto out;
139 		}
140 
141 		r = pthread_attr_setstack(attrp, &stack[i], STACK_SIZE);
142 		if (r != 0) {
143 			printf("pthread_attr_setstack() failed: %d", r);
144 			goto out;
145 		}
146 #endif
147 
148 		r = pthread_create(&ctx[i].thread, attrp, fun, &ctx[i]);
149 		if (r != 0) {
150 			printf("pthread_create failed: %d\n", r);
151 			goto out;
152 		}
153 
154 		printf("%s: socketpair: %d <=> %d\n",
155 			ctx[i].name, ctx[i].spair[0], ctx[i].spair[1]);
156 	}
157 
158 	/* loop until all threads are done */
159 	for (;;) {
160 
161 		/* count threads that are still running and fill in pollfds */
162 		for (i = 0, num_active = 0; i < ARRAY_SIZE(ctx); ++i) {
163 			if (ctx[i].spair[0] == -1) {
164 				continue;
165 			}
166 			fds[num_active].fd = ctx[i].spair[0];
167 			fds[num_active].events = POLLIN;
168 			fds[num_active].revents = 0;
169 			num_active++;
170 		}
171 
172 		if (num_active == 0) {
173 			/* all threads are done */
174 			break;
175 		}
176 
177 		poll_r = poll(fds, num_active, -1);
178 
179 		for (size_t i = 0; i < num_active; ++i) {
180 
181 			fd = fds[i].fd;
182 			idx = fd_to_idx(fd, ctx, ARRAY_SIZE(ctx));
183 			if (idx < 0) {
184 				printf("failed to map fd %d to index\n", fd);
185 				continue;
186 			}
187 
188 			if ((fds[i].revents & POLLIN) != 0) {
189 
190 				memset(buf, '\0', sizeof(buf));
191 
192 				/* read(2) should be used after #25443 */
193 				r = recv(fd, buf, sizeof(buf), 0);
194 				printf("fd: %d: read %d bytes\n", fd, r);
195 			}
196 
197 			if ((fds[i].revents & POLLERR) != 0) {
198 				printf("fd: %d: error\n", fd);
199 			}
200 
201 			if ((fds[i].revents & POLLHUP) != 0) {
202 				printf("fd: %d: hung up\n", fd);
203 				close(ctx[idx].spair[0]);
204 				printf("%s: closed fd %d\n", __func__,
205 					ctx[idx].spair[0]);
206 				pthread_join(ctx[idx].thread, &unused);
207 				printf("joined %s\n", ctx[idx].name);
208 				ctx[idx].spair[0] = -1;
209 			}
210 		}
211 	}
212 
213 	printf("finished!\n");
214 
215 out:
216 #ifndef __ZEPHYR__
217 	return 0;
218 #else
219 	return;
220 #endif
221 }
222