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