]>
Commit | Line | Data |
---|---|---|
5c52c06c DL |
1 | /* |
2 | * Pull-driven write event handler | |
3 | * Copyright (C) 2019 David Lamparter | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the Free | |
7 | * Software Foundation; either version 2 of the License, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; see the file COPYING; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
20 | #ifndef _WRITEPOLL_H | |
21 | #define _WRITEPOLL_H | |
22 | ||
23 | #include <stdbool.h> | |
24 | #include <stdint.h> | |
25 | ||
26 | #include "thread.h" | |
27 | #include "stream.h" | |
28 | ||
29 | struct pullwr; | |
30 | ||
31 | /* This is a "pull-driven" write event handler. Instead of having some buffer | |
32 | * or being driven by the availability of data, it triggers on the space being | |
33 | * available on the socket for data to be written on and then calls fill() to | |
34 | * get data to be sent. | |
35 | * | |
36 | * pullwr_* maintains an "idle" vs. "active" state, going into idle when a | |
37 | * fill() call completes without feeing more data into it. The overall | |
38 | * semantics are: | |
39 | * - to put data out, call pullwr_write(). This is possible from both inside | |
40 | * fill() callbacks or anywhere else. Doing so puts the pullwr into | |
41 | * active state. | |
42 | * - in active state, the fill() callback will be called and should feed more | |
43 | * data in. It should NOT loop to push out more than one "unit" of data; | |
44 | * the pullwr code handles this by calling fill() until it has enough data. | |
45 | * - if there's nothing more to be sent, fill() returns without doing anything | |
46 | * and pullwr goes into idle state after flushing all buffered data out. | |
47 | * - when new data becomes available, pullwr_bump() should be called to put | |
48 | * the pullwr back into active mode so it will collect data from fill(), | |
49 | * or you can directly call pullwr_write(). | |
50 | * - only calling pullwr_write() from within fill() is the cleanest way of | |
51 | * doing things. | |
52 | * | |
53 | * When the err() callback is called, the pullwr should be considered unusable | |
54 | * and released with pullwr_del(). This can be done from inside the callback, | |
55 | * the pullwr code holds no more references on it when calling err(). | |
56 | */ | |
57 | extern struct pullwr *_pullwr_new(struct thread_master *tm, int fd, | |
58 | void *arg, | |
59 | void (*fill)(void *, struct pullwr *), | |
60 | void (*err)(void *, struct pullwr *, bool eof)); | |
61 | extern void pullwr_del(struct pullwr *pullwr); | |
62 | ||
63 | /* type-checking wrapper. makes sure fill() and err() take a first argument | |
64 | * whose type is identical to the type of arg. | |
65 | * => use "void fill(struct mystruct *arg, ...)" - no "void *arg" | |
66 | */ | |
67 | #define pullwr_new(tm, fd, arg, fill, err) ({ \ | |
68 | void (*fill_typechk)(typeof(arg), struct pullwr *) = fill; \ | |
69 | void (*err_typechk)(typeof(arg), struct pullwr *, bool) = err; \ | |
70 | _pullwr_new(tm, fd, arg, (void *)fill_typechk, (void *)err_typechk); \ | |
71 | }) | |
72 | ||
73 | /* max_spin_usec is the time after which the pullwr event handler will stop | |
74 | * trying to get more data from fill() and yield control back to the | |
75 | * thread_master. It does reschedule itself to continue later; this is | |
76 | * only to make sure we don't freeze the entire process if we're piping a | |
77 | * lot of data to a local endpoint that reads quickly (i.e. no backpressure) | |
78 | * | |
79 | * default: 2500 (2.5 ms) | |
80 | * | |
81 | * write_threshold is the amount of data buffered from fill() calls at which | |
82 | * the pullwr code starts calling write(). But this is not a "limit". | |
83 | * pullwr will keep poking fill() for more data until | |
84 | * (a) max_spin_usec is reached; fill() will be called again later after | |
85 | * returning to the thread_master to give other events a chance to run | |
86 | * (b) fill() returns without pushing any data onto the pullwr with | |
87 | * pullwr_write(), so fill() will NOT be called again until a call to | |
88 | * pullwr_bump() or pullwr_write() comes in. | |
89 | * | |
90 | * default: 16384 (16 kB) | |
91 | * | |
92 | * passing 0 for either value (or not calling it at all) uses the default. | |
93 | */ | |
94 | extern void pullwr_cfg(struct pullwr *pullwr, int64_t max_spin_usec, | |
95 | size_t write_threshold); | |
96 | ||
97 | extern void pullwr_bump(struct pullwr *pullwr); | |
98 | extern void pullwr_write(struct pullwr *pullwr, | |
99 | const void *data, size_t len); | |
100 | ||
101 | static inline void pullwr_write_stream(struct pullwr *pullwr, | |
102 | struct stream *s) | |
103 | { | |
104 | pullwr_write(pullwr, s->data, stream_get_endp(s)); | |
105 | } | |
106 | ||
107 | extern void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, | |
108 | size_t *pending, size_t *kernel_pending); | |
109 | ||
110 | #endif /* _WRITEPOLL_H */ |