Skip to content

Commit ef0c73c

Browse files
committed
Add pthreads examples
1 parent 0bd5124 commit ef0c73c

File tree

10 files changed

+319
-0
lines changed

10 files changed

+319
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
int shared_global;
9+
pthread_mutex_t signal_mutex;
10+
pthread_cond_t condition;
11+
12+
void initialise_condition(void)
13+
{
14+
pthread_mutexattr_t *mutex_attributes = NULL;
15+
int pthread_mutex_init_error =
16+
pthread_mutex_init(&signal_mutex, mutex_attributes);
17+
assert(!pthread_mutex_init_error);
18+
19+
pthread_condattr_t *condition_attributes = NULL;
20+
int condition_init_error =
21+
pthread_cond_init(&condition, condition_attributes);
22+
assert(!condition_init_error);
23+
}
24+
25+
// Wait for signal. Note that pthreads requires us to lock and unlock the mutex
26+
// around the call to `pthread_cond_wait`.
27+
void wait(void)
28+
{
29+
const int lock_error = pthread_mutex_lock(&signal_mutex);
30+
assert(!lock_error);
31+
const int wait_error = pthread_cond_wait(&condition, &signal_mutex);
32+
assert(!wait_error);
33+
const int unlock_error = pthread_mutex_unlock(&signal_mutex);
34+
assert(!unlock_error);
35+
}
36+
37+
void signal(void)
38+
{
39+
const int lock_error = pthread_mutex_lock(&signal_mutex);
40+
assert(!lock_error);
41+
int signal_error = pthread_cond_signal(&condition);
42+
assert(!signal_error);
43+
const int unlock_error = pthread_mutex_unlock(&signal_mutex);
44+
assert(!unlock_error);
45+
}
46+
47+
void *worker(void *arguments)
48+
{
49+
shared_global = 42;
50+
signal();
51+
pthread_exit(NULL);
52+
}
53+
54+
pthread_t start_worker_thread(void)
55+
{
56+
pthread_t worker_thread;
57+
const pthread_attr_t *const attributes = NULL;
58+
void *const worker_argument = NULL;
59+
const int create_status =
60+
pthread_create(&worker_thread, attributes, &worker, worker_argument);
61+
assert(create_status == 0);
62+
return worker_thread;
63+
}
64+
65+
void join_thread(const pthread_t thread)
66+
{
67+
const int join_status = pthread_join(thread, NULL);
68+
assert(join_status == 0);
69+
}
70+
71+
int main(void)
72+
{
73+
shared_global = 0;
74+
const pthread_t worker_thread1 = start_worker_thread();
75+
wait();
76+
assert(shared_global == 42);
77+
join_thread(worker_thread1);
78+
79+
pthread_mutex_destroy(&signal_mutex);
80+
pthread_cond_destroy(&condition);
81+
return EXIT_SUCCESS;
82+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FUTURE
2+
test.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test that signals can be used to ensure that the `shared_global` is set before
12+
the assertion on its value is reached.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <assert.h>
2+
#include <pthread.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
6+
void *worker(void *arguments)
7+
{
8+
int *return_value = malloc(sizeof(int));
9+
*return_value = 42;
10+
pthread_exit(return_value);
11+
}
12+
13+
int main(void)
14+
{
15+
pthread_t thread;
16+
const pthread_attr_t *const attributes = NULL;
17+
void *const worker_argument = NULL;
18+
int create_status =
19+
pthread_create(&thread, attributes, &worker, worker_argument);
20+
assert(create_status == 0);
21+
int *worker_return;
22+
int join_status = pthread_join(thread, (void **)&worker_return);
23+
assert(join_status == 0);
24+
printf("Thread return value is %d.\n", *worker_return);
25+
assert(*worker_return == 42);
26+
free(worker_return);
27+
return EXIT_SUCCESS;
28+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FUTURE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test that the assertion that the worker thread return value acquired by
12+
pthread_join is the expected hardcoded value cannot be violated.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
int shared_count = 0;
9+
10+
void *worker(void *arguments)
11+
{
12+
for(int i = 0; i < 100; ++i)
13+
{
14+
int shared_count_copy = shared_count;
15+
// The following call to yield is here in order to cause a thread swap
16+
// during concrete execution in order to demonstrate unsoundness.
17+
sched_yield();
18+
++shared_count_copy;
19+
shared_count = shared_count_copy;
20+
}
21+
pthread_exit(NULL);
22+
}
23+
24+
pthread_t start_worker_thread(void)
25+
{
26+
pthread_t worker_thread;
27+
const pthread_attr_t *const attributes = NULL;
28+
void *const worker_argument = NULL;
29+
const int create_status =
30+
pthread_create(&worker_thread, attributes, &worker, worker_argument);
31+
assert(create_status == 0);
32+
return worker_thread;
33+
}
34+
35+
void join_thread(const pthread_t thread)
36+
{
37+
const int join_status = pthread_join(thread, NULL);
38+
assert(join_status == 0);
39+
}
40+
41+
int main(void)
42+
{
43+
const pthread_t worker_thread1 = start_worker_thread();
44+
const pthread_t worker_thread2 = start_worker_thread();
45+
join_thread(worker_thread1);
46+
join_thread(worker_thread2);
47+
48+
// Check if the shared count has been incremented 200 times.
49+
printf("The shared count is %d.\n", shared_count);
50+
assert(shared_count == 200);
51+
return EXIT_SUCCESS;
52+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FUTURE
2+
no_mutex.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test that cbmc can detect the unsafe shared global access. This is demonstrated
12+
by the violation of the final assertion that the final value of the shared count
13+
is 200.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
int shared_count = 0;
9+
pthread_mutex_t mutex;
10+
11+
void *worker(void *arguments)
12+
{
13+
for(int i = 0; i < 100; ++i)
14+
{
15+
const int lock_error = pthread_mutex_lock(&mutex);
16+
assert(!lock_error);
17+
int shared_count_copy = shared_count;
18+
// The following call to yield is here in order to cause a thread swap
19+
// during concrete execution in order to demonstrate unsoundness.
20+
sched_yield();
21+
++shared_count_copy;
22+
shared_count = shared_count_copy;
23+
const int unlock_error = pthread_mutex_unlock(&mutex);
24+
assert(!unlock_error);
25+
}
26+
pthread_exit(NULL);
27+
}
28+
29+
pthread_t start_worker_thread(void)
30+
{
31+
pthread_t worker_thread;
32+
const pthread_attr_t *const attributes = NULL;
33+
void *const worker_argument = NULL;
34+
const int create_status =
35+
pthread_create(&worker_thread, attributes, &worker, worker_argument);
36+
assert(create_status == 0);
37+
return worker_thread;
38+
}
39+
40+
void join_thread(const pthread_t thread)
41+
{
42+
const int join_status = pthread_join(thread, NULL);
43+
assert(join_status == 0);
44+
}
45+
46+
int main(void)
47+
{
48+
pthread_mutexattr_t *mutex_attributes = NULL;
49+
int pthread_mutex_init_error = pthread_mutex_init(&mutex, mutex_attributes);
50+
assert(!pthread_mutex_init_error);
51+
52+
const pthread_t worker_thread1 = start_worker_thread();
53+
const pthread_t worker_thread2 = start_worker_thread();
54+
join_thread(worker_thread1);
55+
join_thread(worker_thread2);
56+
57+
// Check if the shared count has been incremented 200 times.
58+
printf("The shared count is %d.\n", shared_count);
59+
assert(shared_count == 200);
60+
61+
pthread_mutex_destroy(&mutex);
62+
return EXIT_SUCCESS;
63+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FUTURE
2+
with_mutex.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test that adding an appropriate mutex to the `no_mutex.c` example leads to cbmc
12+
considering the program to be sound.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <assert.h>
2+
#include <pthread.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
6+
pthread_t worker_self_thread;
7+
8+
void *worker(void *arguments)
9+
{
10+
// Check pthread_self() returns a consistent value.
11+
const pthread_t self1 = pthread_self();
12+
const pthread_t self2 = pthread_self();
13+
assert(pthread_equal(self1, self2));
14+
15+
worker_self_thread = self1;
16+
pthread_exit(NULL);
17+
}
18+
19+
int main(void)
20+
{
21+
pthread_t worker_thread;
22+
const pthread_attr_t *const attributes = NULL;
23+
void *const worker_argument = NULL;
24+
const int create_status =
25+
pthread_create(&worker_thread, attributes, &worker, worker_argument);
26+
assert(create_status == 0);
27+
const int join_status = pthread_join(worker_thread, NULL);
28+
assert(join_status == 0);
29+
30+
// Check main thread self is different from worker thread self.
31+
const pthread_t main_thread = pthread_self();
32+
assert(!pthread_equal(main_thread, worker_self_thread));
33+
return EXIT_SUCCESS;
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FUTURE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
pointer handling for concurrency is unsound
10+
--
11+
Test sanity of values returned by pthread_self and pthread_equal.

0 commit comments

Comments
 (0)