forked from siriobalmelli/nonlibc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
epoll_track_test_destructor.c
145 lines (121 loc) · 3.2 KB
/
epoll_track_test_destructor.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
/* epoll_track_test_destructor.c
* Test the use of destructors with epoll_track;
* verify expected behavior and that no leaks develop.
* (c) 2018 Sirio Balmelli
*/
#include <epoll_track.h>
#include <ndebug.h>
#include <unistd.h>
/* A stereotypical "subsystem", which has:
* - a struct for resource tracking (including allocation)
* - one or more fd's for polled I/O
* - specific new() and free() functions which must be called to manage resources
*/
struct subsys {
int infd;
int outfd;
size_t resource_sz;
char *resource;
const char *name;
};
/* subsys_free()
* NOTE the use of a 'void *' 'arg' as opposed to explicitly typed function pointer.
* This is 99% semantics, and 1% avoiding having to choose between a cast
* and a compiler warning when passing &subsys_free to eptk_register().
*/
void subsys_free(void *arg)
{
if (!arg)
return;
struct subsys *sub = arg;
if (sub->infd > 0)
close(sub->infd);
if (sub->outfd > 0)
close(sub->outfd);
free(sub->resource);
free(sub);
}
/* subsys_new()
*/
struct subsys *subsys_new(int infd, int outfd, const char *name)
{
struct subsys *ret = NULL;
NB_die_if(!(
ret = calloc(1, sizeof(*ret))
), "fail alloc size %zu", sizeof(*ret));
ret->infd = infd;
ret->outfd = outfd;
ret->name = name;
ret->resource_sz = 42;
NB_die_if(!(
ret->resource = malloc(ret->resource_sz)
), "fail alloc size %zu", ret->resource_sz);
return ret;
die:
subsys_free(ret);
return NULL;
}
/* subsys_callback()
*/
int subsys_callback(int fd, uint32_t events, void *context)
{
struct subsys *sub = context;
ssize_t res = read(fd, sub->resource, sub->resource_sz);
/* Error or closure means we should close and deallocate;
* this is done when eptk_remove() calls our destructor.
*/
if (res <= 0)
return 1;
ssize_t check = write(sub->outfd, sub->resource, res);
NB_err_if(res != check, "I/O fail");
sub->resource[res] = '\0'; /* force string termination */
NB_inf("%s received '%s'", sub->name, sub->resource);
return 0;
}
/* main()
*/
int main()
{
/* use global err_cnt, so that callbacks can easily report failure
* int err_cnt = 0;
*/
struct epoll_track *tk = NULL;
struct subsys *suba = NULL;
int us2a[2] = { -1, -1 };
int a2us[2] = { -1, -1 };
NB_die_if(!(
tk = eptk_new()
), "");
/* plumbing */
NB_die_if(
pipe(us2a) || pipe(a2us)
, "pipe() failed");
NB_die_if(!(
suba = subsys_new(us2a[0], a2us[1], "sub-a")
), "");
NB_die_if(
eptk_register(tk, us2a[0], EPOLLIN, &subsys_callback, suba, &subsys_free)
, "");
/* write data to pipe and run subsystem */
char buf[] = "hello";
NB_die_if((
write(us2a[1], buf, sizeof(buf))
) != sizeof(buf), "fail write size %zu", sizeof(buf));
eptk_pwait_exec(tk, 1, NULL);
/* verify subsystem ran */
char check[sizeof(buf)] = { '\0' };
ssize_t ret = read(a2us[0], check, sizeof(buf));
NB_die_if(ret != sizeof(buf),
"read %zu but expect %zu", ret, sizeof(buf));
NB_die_if(strcmp(buf, check),
"buf '%s' != check '%s'", buf, check);
/* trigger closures and run epoll subsystems */
close(us2a[1]);
eptk_pwait_exec(tk, 1, NULL);
/* verify subsystem deregistered itself */
NB_die_if(eptk_count(tk),
"%zu subsystems have not exited", eptk_count(tk));
die:
eptk_free(tk);
return err_cnt;
}