-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfd_pool.c
More file actions
239 lines (218 loc) · 7.34 KB
/
fd_pool.c
File metadata and controls
239 lines (218 loc) · 7.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Copyright 2020 Bonedaddy (Alexandre Trottier)
//
// licensed under GNU AFFERO GENERAL PUBLIC LICENSE;
// you may not use this file except in compliance with the License;
// You may obtain the license via the LICENSE file in the repository root;
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fd_pool.h"
#include <arpa/inet.h>
#include <errno.h>
#include <malloc.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#pragma GCC diagnostic ignored "-Wunused-parameter"
/*!
* @brief allocates memory for, and initializes a new fd_pool_t object
* @return Success: pointer to instance of fd_pool_t
* @return Failure: NULL ptr
*/
fd_pool_t *new_fd_pool_t(void) {
fd_pool_t *fpool = calloc(1, sizeof(fd_pool_t));
if (fpool == NULL) {
return NULL;
}
FD_ZERO(&fpool->tcp_set);
FD_ZERO(&fpool->udp_set);
pthread_rwlock_init(&fpool->tcp_lock, NULL);
pthread_rwlock_init(&fpool->udp_lock, NULL);
return fpool;
}
/*!
* @brief polls all tcp or udp fds to determine which can be used for read/write
* @param check_set pointer to an fd_set variable which we will write the active
* fds into
* @param check_set note that this set will be zero'd within the function call
* @param tcp if true check tcp_set, if false check udp_set
* @param read if true only check for read sockets, if false only check for
* write sockets
* @return number of fds
* @todo enable supplying custom timeouts
*/
int get_active_fd_pool_t(fd_pool_t *fpool, fd_set *check_set, bool tcp, bool read) {
int max_fds = 0;
if (tcp) {
pthread_rwlock_rdlock(&fpool->tcp_lock);
unsafe_copy_fd_pool_t(fpool, check_set, true);
max_fds = unsafe_max_socket_fd_pool_t(fpool, true);
pthread_rwlock_unlock(&fpool->tcp_lock);
} else {
pthread_rwlock_rdlock(&fpool->udp_lock);
unsafe_copy_fd_pool_t(fpool, check_set, false);
max_fds = unsafe_max_socket_fd_pool_t(fpool, false);
pthread_rwlock_unlock(&fpool->udp_lock);
}
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int num_active = 0;
if (read) {
num_active = select(max_fds + 1, check_set, NULL, NULL, &timeout);
} else {
num_active = select(max_fds + 1, NULL, check_set, NULL, &timeout);
}
if (num_active < 0) {
printf("socket select failed with error %s\n", strerror(errno));
}
return num_active;
}
/*!
* @brief returns the file descriptors from tcp_set or udp_set, without checking
* to see if any are available for read/write
* @param buffer location in memory to write available fds into, for memory
* efficiency use a stack-alloc'd array
* @param buffer_len the number of items the array can store, this means we will
* get no more than this number of available fds
* @param tcp if true check tcp_set, if false check udp_set
*/
int get_all_fd_pool_t(fd_pool_t *fpool, int *buffer, size_t buffer_len, bool tcp) {
size_t num_items = 0;
if (tcp) {
pthread_rwlock_rdlock(&fpool->tcp_lock);
// https://stackoverflow.com/questions/53698995/how-to-access-fds-in-fd-set-through-indexing-in-linux/53699058#53699058
// must not use value greater than FD_SETSIZE otherwise this will result in
// undefined behavior
for (int i = 1; i <= FD_SETSIZE - 1; i++) {
if (FD_ISSET(i, &fpool->tcp_set)) {
buffer[num_items] = i;
num_items += 1;
if (num_items == buffer_len) {
break;
}
}
}
pthread_rwlock_unlock(&fpool->tcp_lock);
} else {
pthread_rwlock_rdlock(&fpool->udp_lock);
// //
// https://stackoverflow.com/questions/53698995/how-to-access-fds-in-fd-set-through-indexing-in-linux/53699058#53699058
for (int i = 1; i <= FD_SETSIZE - 1; i++) {
if (FD_ISSET(i, &fpool->udp_set)) {
buffer[num_items] = i;
num_items += 1;
if (num_items == buffer_len) {
break;
}
}
}
pthread_rwlock_unlock(&fpool->udp_lock);
}
return (int)num_items;
}
/*!
* @brief returns the highest socket number
* @warning caller must handle locking of the mutexes
*/
int unsafe_max_socket_fd_pool_t(fd_pool_t *fpool, bool tcp) {
int max = 0;
if (tcp) {
for (int i = 1; i < FD_SETSIZE - 1; i++) {
if (FD_ISSET(i, &fpool->tcp_set)) {
if (i > max) {
max = i;
}
}
}
} else {
for (int i = 1; i < FD_SETSIZE - 1; i++) {
if (FD_ISSET(i, &fpool->udp_set)) {
if (i > max) {
max = i;
}
}
}
}
return max;
}
/*!
* @brief checks to see if we have the given fd as part of our pool
* @param is_tcp if true check tcp_set, if false check udp_set
*/
bool is_set_fd_pool_t(fd_pool_t *fpool, int fd, bool is_tcp) {
bool set = false;
if (is_tcp) {
pthread_rwlock_rdlock(&fpool->tcp_lock);
set = FD_ISSET(fd, &fpool->tcp_set);
pthread_rwlock_unlock(&fpool->tcp_lock);
} else {
pthread_rwlock_rdlock(&fpool->udp_lock);
set = FD_ISSET(fd, &fpool->udp_set);
pthread_rwlock_unlock(&fpool->udp_lock);
}
return set;
}
/*!
* @brief copies either tcp or udp fdset
* @warning caller must handle locking of the mutexes
*/
void unsafe_copy_fd_pool_t(fd_pool_t *fpool, fd_set *dst, bool tcp) {
// zero the dest buffer
FD_ZERO(dst);
if (tcp) {
for (int i = 1; i < FD_SETSIZE - 1; i++) {
if (FD_ISSET(i, &fpool->tcp_set)) {
FD_SET(i, dst);
}
}
} else {
for (int i = 1; i < FD_SETSIZE - 1; i++) {
if (FD_ISSET(i, &fpool->udp_set)) {
FD_SET(i, dst);
}
}
}
}
/*!
* @param fd the file descriptor to ste within the pool
* @param is_tcp if true check tcp_set, if false check udp_set
*/
void set_fd_pool_t(fd_pool_t *fpool, int fd, bool is_tcp) {
if (is_tcp == true) {
pthread_rwlock_wrlock(&fpool->tcp_lock);
FD_SET(fd, &fpool->tcp_set);
fpool->num_tcp_fds += 1;
pthread_rwlock_unlock(&fpool->tcp_lock);
} else {
pthread_rwlock_wrlock(&fpool->udp_lock);
FD_SET(fd, &fpool->udp_set);
fpool->num_udp_fds += 1;
pthread_rwlock_unlock(&fpool->udp_lock);
}
}
/*!
* @brief free up all resources allocated for the fd_pool_t struct
* @note this does not close the file resources associated with any file
* descriptors
*/
void free_fd_pool_t(fd_pool_t *fpool) {
// lock to prevent any access to struct fields during free
pthread_rwlock_wrlock(&fpool->tcp_lock);
pthread_rwlock_wrlock(&fpool->udp_lock);
FD_ZERO(&fpool->tcp_set);
FD_ZERO(&fpool->udp_set);
pthread_rwlock_destroy(&fpool->tcp_lock);
pthread_rwlock_destroy(&fpool->udp_lock);
free(fpool);
}