Skip to content

Commit 8c8ad9b

Browse files
committed
Add test using pthread_rwlock
1 parent 1e43962 commit 8c8ad9b

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define NO_LOCKS
2+
#include "with_lock.c"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FUTURE
2+
with_lock.c
3+
--unwind 10
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test that concurrent reading and and writing to a shared structure allows its
12+
state to become inconsistent.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#include <assert.h>
2+
#include <pthread.h>
3+
#include <sched.h>
4+
#include <stdbool.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
8+
struct sharedt
9+
{
10+
#ifndef NO_LOCKS
11+
pthread_rwlock_t lock;
12+
#endif
13+
int a;
14+
int b;
15+
int c;
16+
};
17+
18+
void set_state1(struct sharedt *shared)
19+
{
20+
shared->a = 1;
21+
shared->b = 2;
22+
// The following call to yield causes a thread switch during concrete
23+
// execution in order to demonstrate unsoundness without locks.
24+
sched_yield();
25+
shared->c = 3;
26+
}
27+
28+
void set_state2(struct sharedt *shared)
29+
{
30+
shared->a = 4;
31+
shared->b = 5;
32+
// The following call to yield causes a thread switch during concrete
33+
// execution in order to demonstrate unsoundness without locks.
34+
sched_yield();
35+
shared->c = 6;
36+
}
37+
38+
void *writer1(void *arguments)
39+
{
40+
struct sharedt *shared = (struct sharedt *)arguments;
41+
for(int i = 0; i < 1000; ++i)
42+
{
43+
#ifndef NO_LOCKS
44+
const int lock_error = pthread_rwlock_wrlock(&(shared->lock));
45+
assert(!lock_error);
46+
#endif
47+
set_state1(shared);
48+
#ifndef NO_LOCKS
49+
const int unlock_error = pthread_rwlock_unlock(&(shared->lock));
50+
assert(!unlock_error);
51+
#endif
52+
}
53+
pthread_exit(NULL);
54+
}
55+
56+
void *writer2(void *arguments)
57+
{
58+
struct sharedt *shared = (struct sharedt *)arguments;
59+
for(int i = 0; i < 1000; ++i)
60+
{
61+
#ifndef NO_LOCKS
62+
const int lock_error = pthread_rwlock_wrlock(&(shared->lock));
63+
assert(!lock_error);
64+
#endif
65+
set_state2(shared);
66+
#ifndef NO_LOCKS
67+
const int unlock_error = pthread_rwlock_unlock(&(shared->lock));
68+
assert(!unlock_error);
69+
#endif
70+
}
71+
pthread_exit(NULL);
72+
}
73+
74+
void *checker(void *arguments)
75+
{
76+
struct sharedt *shared = (struct sharedt *)arguments;
77+
for(int i = 0; i < 1000; ++i)
78+
{
79+
#ifndef NO_LOCKS
80+
const int lock_error = pthread_rwlock_rdlock(&(shared->lock));
81+
assert(!lock_error);
82+
#endif
83+
const bool is_state1 = shared->a == 1 && shared->b == 2 && shared->c == 3;
84+
// The following call to yield causes a thread switch during concrete
85+
// execution in order to demonstrate unsoundness without locks.
86+
sched_yield();
87+
const bool is_state2 = shared->a == 4 && shared->b == 5 && shared->c == 6;
88+
assert(is_state1 != is_state2);
89+
#ifndef NO_LOCKS
90+
const int unlock_error = pthread_rwlock_unlock(&(shared->lock));
91+
assert(!unlock_error);
92+
#endif
93+
printf(is_state1 ? "State1\n" : "State2\n");
94+
}
95+
pthread_exit(NULL);
96+
}
97+
98+
int main(void)
99+
{
100+
struct sharedt shared;
101+
set_state1(&shared);
102+
#ifndef NO_LOCKS
103+
const pthread_rwlockattr_t *init_attributes = NULL;
104+
const int init_error = pthread_rwlock_init(&(shared.lock), init_attributes);
105+
assert(!init_error);
106+
#endif
107+
108+
const pthread_attr_t *const attributes = NULL;
109+
void *const thread_argument = &shared;
110+
pthread_t thread_writer1;
111+
assert(
112+
!pthread_create(&thread_writer1, attributes, &writer1, thread_argument));
113+
pthread_t thread_writer2;
114+
assert(
115+
!pthread_create(&thread_writer2, attributes, &writer2, thread_argument));
116+
117+
pthread_t thread_checker1;
118+
pthread_t thread_checker2;
119+
pthread_t thread_checker3;
120+
assert(
121+
!pthread_create(&thread_checker1, attributes, &checker, thread_argument));
122+
assert(
123+
!pthread_create(&thread_checker2, attributes, &checker, thread_argument));
124+
assert(
125+
!pthread_create(&thread_checker3, attributes, &checker, thread_argument));
126+
127+
assert(!pthread_join(thread_writer1, NULL));
128+
assert(!pthread_join(thread_writer2, NULL));
129+
assert(!pthread_join(thread_checker1, NULL));
130+
assert(!pthread_join(thread_checker2, NULL));
131+
assert(!pthread_join(thread_checker3, NULL));
132+
133+
#ifndef NO_LOCKS
134+
const int destroy_error = pthread_rwlock_destroy(&(shared.lock));
135+
assert(!destroy_error);
136+
#endif
137+
return EXIT_SUCCESS;
138+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FUTURE
2+
with_lock.c
3+
--unwind 10
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test that an appropriate read write lock keeps the state of a shared structure
12+
consistent.

0 commit comments

Comments
 (0)