-
Notifications
You must be signed in to change notification settings - Fork 1
/
thread_context.c
155 lines (129 loc) · 3.25 KB
/
thread_context.c
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
/*
* network latency measurer
* Copyright (C) 2018 Ricardo Biehl Pasquali <pasqualirb@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* 16/05/2018
*
* generic thread set up and routine
*/
#include <poll.h> /* poll() */
#include <pthread.h>
#include <signal.h> /* kill() SIGINT */
#include <string.h> /* memset() */
#include <sys/eventfd.h> /* eventfd */
#include <sys/types.h>
#include <unistd.h> /* close() getpid() */
#include "thread_context.h"
/*
* efd: evend fd (see eventfd.2 manual) here is used to
* signal the thread to exit.
*
* NOTE: About returning -1 on error, poll() may have
* succeed and revents = -1. However it's very unlikely
* the revents mask we get equals -1
*/
static short
do_wait(int efd, int fd, short events)
{
struct pollfd pfd[2];
/* prepare */
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = efd;
pfd[0].events = POLLIN;
pfd[1].fd = fd;
pfd[1].events = events;
/* poll */
if (poll(pfd, 2, -1) == -1)
return -1;
/*
* if true, user have request us to quit
* Do not check _keep_running!
*/
if (pfd[0].revents & POLLIN)
pthread_exit(0);
return pfd[1].revents;
}
static int
generic_thread_run(struct thread_ctx *r)
{
short revents;
/* We're not checking for _keep_running anymore */
while (1) {
/*
* POLLERR is ignored because it's set by
* default. See poll.2 manual
*/
revents = do_wait(r->efd, r->fd, r->events);
if (revents == -1)
goto _go_exit_err;
/* poll again if we haven't woken up with r->events */
if (!(revents & r->events))
continue;
if (r->routine(r->data) == -1)
goto _go_exit_err;
}
return 0;
_go_exit_err:
/* shutdown other threads by sending SIGINT signal to the process */
kill(getpid(), SIGINT);
return -1;
}
static void*
generic_thread_routine(void *data)
{
if (generic_thread_run((struct thread_ctx*) data) == 0)
pthread_exit((void*) 0); /* ok */
else
pthread_exit((void*) 1); /* error */
return NULL;
}
void
thread_context_cleanup(struct thread_ctx *c)
{
close(c->efd);
}
int
thread_context_setup(struct thread_ctx *c, int (*routine)(void*),
void *data, int fd, short events)
{
/* create event file descriptors used to signal threads to exit */
c->efd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE);
if (c->efd == -1)
return -1;
c->routine = routine;
c->data = data;
c->fd = fd;
c->events = events;
return 0;
}
int
thread_terminate(struct thread_ctx *c)
{
void *ret_ptr;
eventfd_write(c->efd, 1);
pthread_join(c->thread, &ret_ptr);
if ((uintptr_t) ret_ptr != 0)
return 1;
return 0;
}
int
thread_start(struct thread_ctx *c)
{
if (pthread_create(&c->thread, NULL, generic_thread_routine, c) != 0)
return -1;
return 0;
}