Improve documentation, improve has_pollin handling.
[~madcoder/pwqr.git] / test / test.c
1 /*
2  * Copyright (C) 2012   Pierre Habouzit <pierre.habouzit@intersec.com>
3  * Copyright (C) 2012   Intersec SAS
4  *
5  * This file is part of the Linux Pthread Workqueue Regulator tests.
6  *
7  * The Linux Pthread Workqueue Regulator is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU Lesser
9  * General Public License as published by the Free Software Foundation,
10  * either version 2.1 of the License, or (at your option) any later version.
11  *
12  * The Linux Pthread Workqueue Regulator is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
14  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with The Linux Pthread Workqueue Regultaor.
19  * If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <assert.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <pthread.h>
27 #include <sched.h>
28 #include <stddef.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <sys/ioctl.h>
32 #include <sys/poll.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #include "../kernel/pwqr.h"
38
39 /* pwqr wrapping {{{ */
40
41 static int pwqr_create(int flags)
42 {
43     if (flags & ~PWQR_FL__SET) {
44         errno = -EINVAL;
45         return -1;
46     }
47     return open("/dev/"PWQR_DEVICE_NAME, O_RDWR | flags);
48 }
49
50 static int pwqr_ctl(int fd, int op, int val, void *uaddr)
51 {
52     struct pwqr_ioc_wait wait;
53
54     switch (op) {
55       case PWQR_CTL_GET_CONC:
56       case PWQR_CTL_REGISTER:
57       case PWQR_CTL_UNREGISTER:
58       case PWQR_CTL_PARK:
59         return ioctl(fd, op);
60       case PWQR_CTL_SET_CONC:
61       case PWQR_CTL_WAKE:
62       case PWQR_CTL_WAKE_OC:
63         return ioctl(fd, op, val);
64       case PWQR_CTL_WAIT:
65         wait.pwqr_ticket = val;
66         wait.pwqr_uaddr  = uaddr;
67         return ioctl(fd, op, &wait);
68       default:
69         errno = EINVAL;
70         return -1;
71     }
72 }
73
74 /* }}} */
75
76 static int pwqr_fd_g = -1;
77 static int pi_g;
78 static int state_g;
79
80 #define trace(fmt, ...)   fprintf(stderr, fmt"\n", ##__VA_ARGS__)
81
82 static void *thr_main(void *_who)
83 {
84     int who = (long)_who;
85
86     if (pwqr_ctl(pwqr_fd_g, PWQR_CTL_REGISTER, 0, NULL) < 0)
87         err(-1, "%d: pwqr_ctl(PWQR_CTL_REGISTER)", who);
88
89     /* busy-loop */
90     trace("%d: loop1", who);
91     do {
92         pthread_testcancel();
93     } while (!state_g);
94
95     trace("%d: tries to park", who);
96     if (pwqr_ctl(pwqr_fd_g, PWQR_CTL_PARK, 0, NULL) < 0)
97         trace("pwqr_ctl(PWQR_CTL_PARK)");
98     trace("%d: unparked", who);
99
100     /* busy-loop */
101     trace("%d: loop2", who);
102     do {
103         pthread_testcancel();
104     } while (state_g);
105
106     return NULL;
107 }
108
109 static uint64_t tv_diff(const struct timeval *t1, const struct timeval *t2)
110 {
111     return (t1->tv_sec - t2->tv_sec) * 1000000ULL + t1->tv_usec - t2->tv_usec;
112 }
113
114 int main(void)
115 {
116     pthread_t tids[32];
117     struct pollfd pfd;
118     struct timeval start, tv;
119     int rc;
120
121     setlinebuf(stderr);
122
123     pwqr_fd_g = pwqr_create(PWQR_FL_NONBLOCK);
124     if (pwqr_fd_g < 0)
125         err(-1, "pwqr_create");
126
127     pi_g = pwqr_ctl(pwqr_fd_g, PWQR_CTL_GET_CONC, 0, NULL);
128     if (pi_g < 1)
129         err(-1, "pwqr_ctl(PWQR_CTL_GET_CONC)");
130
131     for (int i = 0; i < pi_g + 1; i++) {
132         pthread_create(&tids[i], NULL, thr_main, (void *)(long)i);
133     }
134
135     pfd.fd     = pwqr_fd_g;
136     pfd.events = POLLIN;
137     gettimeofday(&start, NULL);
138     for (;;) {
139         pfd.revents = 0;
140         rc = poll(&pfd, 1, -1);
141         if (rc < 0) {
142             warn("poll");
143             continue;
144         }
145         if (rc == 1) {
146             if (pfd.revents & POLLIN) {
147                 if (read(pwqr_fd_g, &rc, sizeof(rc)) != sizeof(rc)) {
148                     warn("read");
149                 } else {
150                     gettimeofday(&tv, NULL);
151                     trace("[%06jd.%06jd] overcommit of %d",
152                           tv_diff(&tv, &start) / 1000000,
153                           tv_diff(&tv, &start) % 1000000, rc);
154                 }
155                 state_g = 1;
156             }
157             if (pfd.revents & POLLHUP) {
158                 errx(-1, "poll returned POLLHUP");
159             }
160         }
161     }
162
163     for (int i = 0; i < pi_g + 1; i++) {
164         pthread_join(tids[i], NULL);
165     }
166     return 0;
167 }