summaryrefslogtreecommitdiff
path: root/ot_batch.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_batch.c')
-rw-r--r--ot_batch.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/ot_batch.c b/ot_batch.c
new file mode 100644
index 0000000..84eb4a8
--- /dev/null
+++ b/ot_batch.c
@@ -0,0 +1,158 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <errno.h>
8#include <limits.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/socket.h>
12#include <sys/uio.h>
13#include <unistd.h>
14
15/* Opentracker */
16#include "ot_batch.h"
17
18#ifndef IOV_MAX
19#define IOV_MAX 1024
20#endif
21
22#define OT_BATCH_MIN_ENTRIES 16
23
24static int batch_room_for(ot_batch *batch, size_t wanted) {
25 size_t space = batch->space ? batch->space : OT_BATCH_MIN_ENTRIES;
26 struct iovec *grown;
27
28 if (batch->count + wanted <= batch->space)
29 return 0;
30
31 /* During a chunked transfer entries are appended while earlier ones are
32 still going out, so the front of the array frees up as we go. Slide the
33 rest down before asking for more. */
34 if (batch->first && batch->count - batch->first + wanted <= batch->space) {
35 memmove(batch->entries, batch->entries + batch->first, (batch->count - batch->first) * sizeof(struct iovec));
36 batch->count -= batch->first;
37 batch->first = 0;
38 return 0;
39 }
40
41 while (space < batch->count + wanted)
42 space *= 2;
43
44 if (!(grown = realloc(batch->entries, space * sizeof(struct iovec))))
45 return -1;
46
47 batch->entries = grown;
48 batch->space = space;
49 return 0;
50}
51
52int batch_add(ot_batch *batch, void *buf, size_t len) {
53 if (batch_room_for(batch, 1)) {
54 free(buf);
55 return -1;
56 }
57
58 batch->entries[batch->count].iov_base = buf;
59 batch->entries[batch->count].iov_len = len;
60 ++batch->count;
61 return 0;
62}
63
64int batch_add_vector(ot_batch *batch, const struct iovec *entries, size_t count) {
65 if (batch_room_for(batch, count)) {
66 while (count--)
67 free(entries[count].iov_base);
68 return -1;
69 }
70
71 memcpy(batch->entries + batch->count, entries, count * sizeof(struct iovec));
72 batch->count += count;
73 return 0;
74}
75
76/* Drop everything the kernel accepted, freeing each buffer as it goes */
77static void batch_consume(ot_batch *batch, size_t written) {
78 while (written) {
79 struct iovec *entry = batch->entries + batch->first;
80 size_t left = entry->iov_len - batch->offset;
81
82 if (written < left) {
83 batch->offset += written;
84 return;
85 }
86
87 free(entry->iov_base);
88 written -= left;
89 batch->offset = 0;
90 ++batch->first;
91 }
92
93 /* Nothing is outstanding, so the array can start over from the front */
94 if (batch->first == batch->count)
95 batch->first = batch->count = 0;
96}
97
98/* A peer that has gone away must come back as an error rather than as a
99 signal that kills the tracker, so ask the kernel not to raise one where it
100 can be asked. macOS cannot, and relies on SIGPIPE being blocked. */
101static ssize_t batch_writev(int s, struct iovec *entries, int count) {
102#ifdef MSG_NOSIGNAL
103 struct msghdr msg;
104
105 memset(&msg, 0, sizeof(msg));
106 msg.msg_iov = entries;
107 msg.msg_iovlen = count;
108 return sendmsg(s, &msg, MSG_NOSIGNAL);
109#else
110 return writev(s, entries, count);
111#endif
112}
113
114ssize_t batch_send(int s, ot_batch *batch) {
115 size_t entries = batch->count - batch->first;
116 ssize_t written;
117
118 if (!batch_pending(batch))
119 return BATCH_DONE;
120
121 if (entries > IOV_MAX)
122 entries = IOV_MAX;
123
124 /* The first entry may be half way out already */
125 batch->entries[batch->first].iov_base = (char *)batch->entries[batch->first].iov_base + batch->offset;
126 batch->entries[batch->first].iov_len -= batch->offset;
127 written = batch_writev(s, batch->entries + batch->first, (int)entries);
128 batch->entries[batch->first].iov_base = (char *)batch->entries[batch->first].iov_base - batch->offset;
129 batch->entries[batch->first].iov_len += batch->offset;
130
131 if (written < 0)
132 return (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) ? BATCH_AGAIN : BATCH_FAILED;
133 if (!written)
134 return BATCH_FAILED;
135
136 batch_consume(batch, (size_t)written);
137 return written;
138}
139
140void batch_reset(ot_batch *batch) {
141 while (batch->first < batch->count)
142 free(batch->entries[batch->first++].iov_base);
143
144 free(batch->entries);
145 batch->entries = NULL;
146 batch->count = batch->first = batch->offset = batch->space = 0;
147}
148
149/* For the few constants the chunked encoding needs, which the batch frees
150 along with everything else */
151int batch_add_copy(ot_batch *batch, const void *buf, size_t len) {
152 void *copy = malloc(len);
153
154 if (!copy)
155 return -1;
156 memcpy(copy, buf, len);
157 return batch_add(batch, copy, len);
158}