The Pedigree Project 0.1
xattr-contract-test/concurrency.c
1#define _GNU_SOURCE
2#include <pthread.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/xattr.h>
8
9enum { CREATE, REPLACE, REMOVE, WRITE_MANY, READ_MANY };
10struct contender {
11 int fd, ready, gate, operation;
12 const unsigned char* value;
13 size_t length;
14 int result, error;
15};
16
17static unsigned char short_value[191], long_value[257];
18
19static void* compete(void* opaque) {
20 struct contender* item = opaque;
21 item->result = -1;
22 item->error = EIO;
23 if (xa_send(item->ready, 'r') || xa_receive(item->gate, 'g'))
24 return NULL;
25 if (item->operation == REMOVE) {
26 item->result = fremovexattr(item->fd, "user.race");
27 item->error = errno;
28 } else if (item->operation == CREATE || item->operation == REPLACE) {
29 item->result = fsetxattr(item->fd, "user.race", item->value, item->length,
30 item->operation == CREATE ? XATTR_CREATE : XATTR_REPLACE);
31 item->error = errno;
32 } else {
33 item->result = 0;
34 item->error = 0;
35 for (int n = 0; n < 64; ++n) {
36 if (item->operation == WRITE_MANY) {
37 const void* value = (n & 1) ? (const void*)short_value : long_value;
38 size_t length = (n & 1) ? sizeof(short_value) : sizeof(long_value);
39 if (fsetxattr(item->fd, "user.race", value, length, XATTR_REPLACE)) {
40 item->result = -1;
41 item->error = errno;
42 break;
43 }
44 } else {
45 unsigned char buffer[sizeof(long_value) + 1];
46 memset(buffer, 0xa5, sizeof(buffer));
47 ssize_t length = fgetxattr(item->fd, "user.race", buffer, sizeof(buffer));
48 int valid = (length == sizeof(short_value) && !memcmp(buffer, short_value, length)) ||
49 (length == sizeof(long_value) && !memcmp(buffer, long_value, length));
50 if (!valid || buffer[length] != 0xa5) {
51 item->result = -1;
52 item->error = length < 0 ? errno : EIO;
53 break;
54 }
55 }
56 }
57 }
58 return NULL;
59}
60
61static int pair(struct contender* first, struct contender* second) {
62 int failed = 0, ready[2] = {-1, -1}, gate[2] = {-1, -1}, created = 0;
63 pthread_t threads[2];
64 struct contender* items[] = {first, second};
65 CHECK(!pipe(ready) && !pipe(gate));
66 for (int n = 0; n < 2; ++n) {
67 items[n]->ready = ready[1];
68 items[n]->gate = gate[0];
69 CHECK(!pthread_create(&threads[n], NULL, compete, items[n]));
70 ++created;
71 }
72 CHECK(!xa_receive(ready[0], 'r') && !xa_receive(ready[0], 'r'));
73 CHECK(!xa_send(gate[1], 'g') && !xa_send(gate[1], 'g'));
74out:
75 if (gate[1] >= 0) {
76 close(gate[1]);
77 gate[1] = -1;
78 }
79 for (int n = 0; n < created; ++n) {
80 int result = pthread_join(threads[n], NULL);
81 if (result) {
82 fprintf(stderr, "XATTR-CONTRACT: contender join=%d\n", result);
83 failed = 1;
84 }
85 }
86 for (int n = 0; n < 2; ++n) {
87 if (gate[n] >= 0)
88 close(gate[n]);
89 if (ready[n] >= 0)
90 close(ready[n]);
91 }
92 return failed;
93}
94
95static int atomic_updates(int backend) {
96 int failed = 0, alias = -1;
97 struct xa_file file = {.fd = -1};
98 CHECK(!xa_create(&file, backend, 0));
99 CHECK((alias = xa_open_alias(&file)) >= 0);
100 for (int round = 0; round < 4; ++round) {
101 struct contender first = {
102 .fd = file.fd, .operation = CREATE, .value = short_value, .length = sizeof(short_value)};
103 struct contender second = {
104 .fd = alias, .operation = CREATE, .value = long_value, .length = sizeof(long_value)};
105 CHECK(!pair(&first, &second));
106 CHECK((!first.result && second.result == -1 && second.error == EEXIST) ||
107 (!second.result && first.result == -1 && first.error == EEXIST));
108 struct contender* winner = !first.result ? &first : &second;
109 CHECK(!xa_value(&file, XA_FD, "user.race", winner->value, winner->length));
110 first.operation = REPLACE;
111 second.operation = REPLACE;
112 CHECK(!pair(&first, &second));
113 CHECK(!first.result && !second.result);
114 unsigned char buffer[sizeof(long_value)];
115 ssize_t length = fgetxattr(alias, "user.race", buffer, sizeof(buffer));
116 CHECK((length == sizeof(short_value) && !memcmp(buffer, short_value, length)) ||
117 (length == sizeof(long_value) && !memcmp(buffer, long_value, length)));
118 second.operation = REMOVE;
119 CHECK(!pair(&first, &second));
120 CHECK(!second.result);
121 CHECK(!first.result || (first.result == -1 && first.error == ENODATA));
122 CHECK(fgetxattr(file.fd, "user.race", NULL, 0) == -1 && errno == ENODATA);
123 }
124 CHECK(!fsetxattr(file.fd, "user.race", short_value, sizeof(short_value), XATTR_CREATE));
125 struct contender writer = {.fd = file.fd, .operation = WRITE_MANY};
126 struct contender reader = {.fd = alias, .operation = READ_MANY};
127 CHECK(!pair(&writer, &reader));
128 if (writer.result || reader.result)
129 fprintf(stderr, "XATTR-CONTRACT: snapshots backend=%d writer=%d/%d reader=%d/%d\n", backend,
130 writer.result, writer.error, reader.result, reader.error);
131 CHECK(!writer.result && !reader.result);
132out:
133 if (alias >= 0)
134 close(alias);
135 xa_close(&file);
136 return failed;
137}
138
139int xa_concurrency(void) {
140 for (size_t n = 0; n < sizeof(short_value); ++n)
141 short_value[n] = (unsigned char)(n * 17 + 3);
142 for (size_t n = 0; n < sizeof(long_value); ++n)
143 long_value[n] = (unsigned char)(n * 31 + 11);
144 for (int backend = XA_MEMFD; backend <= XA_EXT2; ++backend)
145 if (atomic_updates(backend))
146 return 1;
147 return 0;
148}
Definition waits.c:9