Skip to content

Commit 241002f

Browse files
committed
Add pthreads examples
1 parent 32da699 commit 241002f

File tree

10 files changed

+312
-0
lines changed

10 files changed

+312
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <assert.h>
2+
#include <pthread.h>
3+
#include <stdlib.h>
4+
#include <stdio.h>
5+
#include <stdbool.h>
6+
#include <sched.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 = pthread_mutex_init(&signal_mutex, mutex_attributes);
16+
assert(!pthread_mutex_init_error);
17+
18+
pthread_condattr_t *condition_attributes = NULL;
19+
int condition_init_error = pthread_cond_init(&condition, condition_attributes);
20+
assert(!condition_init_error);
21+
}
22+
23+
// Wait for signal. Note that pthreads requires us to lock and unlock the mutex
24+
// around the call to `pthread_cond_wait`.
25+
void wait(void)
26+
{
27+
const int lock_error = pthread_mutex_lock(&signal_mutex);
28+
assert(!lock_error);
29+
const int wait_error = pthread_cond_wait(&condition, &signal_mutex);
30+
assert(!wait_error);
31+
const int unlock_error = pthread_mutex_unlock(&signal_mutex);
32+
assert(!unlock_error);
33+
}
34+
35+
void signal(void)
36+
{
37+
const int lock_error = pthread_mutex_lock(&signal_mutex);
38+
assert(!lock_error);
39+
int signal_error = pthread_cond_signal(&condition);
40+
assert(!signal_error);
41+
const int unlock_error = pthread_mutex_unlock(&signal_mutex);
42+
assert(!unlock_error);
43+
}
44+
45+
void *worker(void *arguments)
46+
{
47+
shared_global = 42;
48+
signal();
49+
pthread_exit(NULL);
50+
}
51+
52+
pthread_t start_worker_thread(void)
53+
{
54+
pthread_t worker_thread;
55+
const pthread_attr_t *const attributes = NULL;
56+
void *const worker_argument = NULL;
57+
const int create_status =
58+
pthread_create(&worker_thread, attributes, &worker, worker_argument);
59+
assert(create_status == 0);
60+
return worker_thread;
61+
}
62+
63+
void join_thread(const pthread_t thread)
64+
{
65+
const int join_status = pthread_join(thread, NULL);
66+
assert(join_status == 0);
67+
}
68+
69+
int main(void)
70+
{
71+
shared_global = 0;
72+
const pthread_t worker_thread1 = start_worker_thread();
73+
wait();
74+
assert(shared_global == 42);
75+
join_thread(worker_thread1);
76+
77+
pthread_mutex_destroy(&signal_mutex);
78+
pthread_cond_destroy(&condition);
79+
return EXIT_SUCCESS;
80+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FUTURE
2+
test.c
3+
^EXIT=0$
4+
^SIGNAL=0$
5+
^VERIFICATION SUCCESSFUL$
6+
--
7+
^warning: ignoring
8+
pointer handling for concurrency is unsound
9+
--
10+
Test that signals can be used to ensure that the `shared_global` is set before
11+
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 <stdlib.h>
4+
#include <stdio.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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FUTURE
2+
main.c
3+
^EXIT=0$
4+
^SIGNAL=0$
5+
^VERIFICATION SUCCESSFUL$
6+
--
7+
^warning: ignoring
8+
pointer handling for concurrency is unsound
9+
--
10+
Test that the assertion that the worker thread return value acquired by
11+
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 <stdlib.h>
4+
#include <stdio.h>
5+
#include <stdbool.h>
6+
#include <sched.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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FUTURE
2+
no_mutex.c
3+
^EXIT=0$
4+
^SIGNAL=0$
5+
^VERIFICATION FAILED$
6+
--
7+
^warning: ignoring
8+
pointer handling for concurrency is unsound
9+
--
10+
Test that cbmc can detect the unsafe shared global access. This is demonstrated
11+
by the violation of the final assertion that the final value of the shared count
12+
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 <stdlib.h>
4+
#include <stdio.h>
5+
#include <stdbool.h>
6+
#include <sched.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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FUTURE
2+
with_mutex.c
3+
^EXIT=0$
4+
^SIGNAL=0$
5+
^VERIFICATION SUCCESSFUL$
6+
--
7+
^warning: ignoring
8+
pointer handling for concurrency is unsound
9+
--
10+
Test that adding an appropriate mutex to the `no_mutex.c` example leads to cbmc
11+
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 <stdlib.h>
4+
#include <stdio.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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FUTURE
2+
main.c
3+
^EXIT=0$
4+
^SIGNAL=0$
5+
^VERIFICATION SUCCESSFUL$
6+
--
7+
^warning: ignoring
8+
pointer handling for concurrency is unsound
9+
--
10+
Test sanity of values returned by pthread_self and pthread_equal.

0 commit comments

Comments
 (0)