9#include <sys/sendfile.h>
15 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
17void tf_pause(
int milliseconds) {
18 struct timespec delay = {milliseconds / 1000, (milliseconds % 1000) * 1000000L};
19 while (nanosleep(&delay, &delay) && errno == EINTR) {
22int tf_wait(
volatile int* flag,
int milliseconds) {
23 const int64_t until = tf_now() + (int64_t)milliseconds * 1000000;
24 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
25 if (tf_now() >= until)
31int tf_reap(pid_t child,
int milliseconds) {
32 const int64_t until = tf_now() + (int64_t)milliseconds * 1000000;
33 while (tf_now() < until) {
35 pid_t result = waitpid(child, &status, WNOHANG);
36 if (result == child) {
37 int code = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
39 fprintf(stderr,
"TRANSFER-CONTRACT: child=%ld status=%d\n", (
long)child, code);
42 if (result < 0 && errno != EINTR)
46 fprintf(stderr,
"TRANSFER-CONTRACT: child=%ld timeout\n", (
long)child);
48 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
52unsigned char tf_pattern(
size_t offset,
int seed) {
53 return seed == TF_FILLER ? 0x7a : ((offset * 17) ^ (offset >> 9) ^ seed) & 255;
55int tf_create(
struct tf_file* file,
const char* directory,
size_t bytes,
int seed) {
56 static unsigned sequence;
57 snprintf(file->path,
sizeof(file->path),
"%s/transfer-%ld-%u", directory, (
long)getpid(),
59 file->fd = open(file->path, O_CREAT | O_EXCL | O_RDWR, 0600);
62 unsigned char buffer[4096];
63 for (
size_t offset = 0; offset < bytes;) {
64 size_t count = bytes - offset <
sizeof(buffer) ? bytes - offset : sizeof(buffer);
65 for (
size_t n = 0; n < count; ++n)
66 buffer[n] = tf_pattern(offset + n, seed);
67 if (pwrite(file->fd, buffer, count, offset) != (ssize_t)count)
73void tf_destroy(
struct tf_file* file) {
80int tf_verify(
int fd, off_t offset,
size_t length,
size_t source_offset,
int seed) {
81 unsigned char buffer[4096];
82 for (
size_t done = 0; done < length;) {
83 size_t count = length - done <
sizeof(buffer) ? length - done : sizeof(buffer);
84 if (pread(fd, buffer, count, offset + done) != (ssize_t)count)
86 for (
size_t n = 0; n < count; ++n)
87 if (buffer[n] != tf_pattern(source_offset + done + n, seed))
93int tf_socket_read(
int fd,
size_t length,
size_t source_offset,
int seed) {
94 unsigned char buffer[4096];
95 const int64_t until = tf_now() + 5000000000;
96 for (
size_t done = 0; done < length;) {
97 struct pollfd entry = {.fd = fd, .events = POLLIN};
98 if (tf_now() >= until)
100 int ready = poll(&entry, 1, 100);
101 if (ready < 0 && errno == EINTR)
107 size_t count = length - done <
sizeof(buffer) ? length - done : sizeof(buffer);
108 ssize_t received = read(fd, buffer, count);
111 for (ssize_t n = 0; n < received; ++n)
112 if (buffer[n] != tf_pattern(source_offset + done + n, seed))
118ssize_t tf_saturate(
int fd) {
119 int flags = fcntl(fd, F_GETFL);
120 if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK))
123 memset(bytes, 0x7a,
sizeof(bytes));
125 while (total < 1024 * 1024) {
126 ssize_t result = write(fd, bytes,
sizeof(bytes));
127 if (result < 0 && errno == EAGAIN) {
128 if (fcntl(fd, F_SETFL, flags))
136 fcntl(fd, F_SETFL, flags);
139ssize_t tf_copy(
int kind,
int input, off_t* input_offset,
int output, off_t* output_offset,
141 return kind == TF_SENDFILE
142 ? sendfile(output, input, input_offset, count)
143 : copy_file_range(input, input_offset, output, output_offset, count, 0);
145void* tf_worker(
void* argument) {
147 __atomic_store_n(&transfer->ready, 1, __ATOMIC_RELEASE);
148 if (!tf_wait(&transfer->gate, 5000)) {
149 transfer->started = tf_now();
151 transfer->result = tf_copy(transfer->kind, transfer->input, transfer->input_offset,
152 transfer->output, transfer->output_offset, transfer->count);
153 transfer->error = errno;
154 transfer->finished = tf_now();
156 transfer->error = ETIMEDOUT;
157 __atomic_store_n(&transfer->done, 1, __ATOMIC_RELEASE);
160void tf_diagnostic(
const struct tf_transfer* transfer) {
161 int done = __atomic_load_n(&transfer->done, __ATOMIC_ACQUIRE);
163 "TRANSFER-CONTRACT: kind=%d count=%zu done=%d result=%ld/%d start=%lld end=%lld\n",
164 transfer->kind, transfer->count, done, done ? (
long)transfer->result : -2,
165 done ? transfer->error : -2, done ? (long long)transfer->started : -1,
166 done ? (long long)transfer->finished : -1);
168int tf_join(pthread_t thread,
struct tf_transfer* transfer) {
169 __atomic_store_n(&transfer->gate, 1, __ATOMIC_RELEASE);
170 if (tf_wait(&transfer->done, 5000)) {
171 tf_diagnostic(transfer);
174 return pthread_join(thread, NULL);
176static int run(
const char* name,
int (*test)(
void)) {
177 printf(
"TRANSFER-CONTRACT: BEGIN %s\n", name);
179 pid_t child = fork();
187 _exit(result ? 1 : 0);
189 const int status = tf_reap(child, 45000);
190 printf(
"TRANSFER-CONTRACT: %s %s status=%d\n", status ?
"FAIL" :
"PASS", name, status);
194int main(
int argc,
char** argv) {
195 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
200 } suites[] = {{
"file-copy", transfer_file_copy},
201 {
"offsets", transfer_offsets},
202 {
"stream", transfer_stream},
203 {
"lifetime", transfer_lifetime},
204 {
"races", transfer_races}};
206 for (
unsigned n = 0; n <
sizeof(suites) /
sizeof(suites[0]); ++n) {
207 if (argc > 1 && strcmp(argv[1], suites[n].name))
210 if (run(suites[n].name, suites[n].test))
215 puts(
"TRANSFER-CONTRACT: END PASS");