File: main.c

package info (click to toggle)
cbmc 6.6.0-4
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 153,852 kB
  • sloc: cpp: 386,459; ansic: 114,466; java: 28,405; python: 6,003; yacc: 4,552; makefile: 4,041; lex: 2,487; xml: 2,388; sh: 2,050; perl: 557; pascal: 184; javascript: 163; ada: 36
file content (171 lines) | stat: -rw-r--r-- 4,079 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Benchmark built by Alex Horn
// cbmc thread_chain_posix.c: assertion violation since we only use regular threads
// cbmc -D_ENABLE_CHAIN_ --unwind 2 thread_chain_posix.c: no assertion violation
// cbmc -D_ENABLE_CHAIN_ -D_SANITY_CHECK_ --unwind 2 thread_chain_posix.c: assertion violation
#include <pthread.h>
#include <assert.h>

#ifndef _ENABLE_MEMORY_BUG_
#include <stdlib.h>

void *malloc(size_t size);
void free(void* ptr);
#endif

// Define _EXPOSE_BUG_ when _ENABLE_CHAIN_ is undefined in
// order to get an executable that shows the effect of
// using thread_chains instead of regular threads.
#if defined(_EXPOSE_BUG_) || defined(_EXPOSE_SANITY_CHECK_)
#include <unistd.h>

#define TINY_DELAY 1
#define SHORT_DELAY 3
#define LONG_DELAY 5
#endif

// ========= Chain library =========

// This struct is not thread-safe itself!
//
// A thread_chain is like a chain of threads `f, g, ...` where `f`
// must terminate before `g` can start to run, and so forth.
struct thread_chain_t
{
  // last thread in chain
  pthread_t thread;
};

// Public function pointer
typedef void *(*start_routine)(void *);

// Internal
struct thread_chain_args_t
{
  // last thread in chain
  pthread_t thread;

  // function pointer for new thread, never null
  start_routine f;

  // argument to f, possibly null
  void *f_arg;
};

// Internal start routine for a thread in a thread_chain
static void* thread_chain_start_routine(void *ptr)
{
  struct thread_chain_args_t *args = (struct thread_chain_args_t *) ptr;

  // ignore errors
  pthread_join(args->thread, NULL);

  assert(args->f != NULL);
  void* r = args->f(args->f_arg);

#ifndef _ENABLE_MEMORY_BUG_
  free(args);
#endif

  pthread_exit(NULL);
  return r;
}

// This function is not thread-safe itself!
//
// Create a new thread for `f` where `f(arg)` starts to run as
// soon as the currently running `self->thread` has terminated.
//
// \post: `self->thread` is the newly created thread for `f`
int thread_chain_run(struct thread_chain_t *self, start_routine f, void *arg)
{
#ifdef _ENABLE_MEMORY_BUG_
  struct thread_chain_args_t local_args;
  struct thread_chain_args_t *args = &local_args;
#else
  // since the address of a local variable results
  // in undefined behaviour when thread_chain_run returns,
  // we allocate a thread_chain_args_t struct instead in
  // case thread_chain_start_routine outlives thread_chain_run.
  struct thread_chain_args_t *args = malloc(sizeof(struct thread_chain_args_t));
  if (!args)
    return -1;
#endif

  args->thread = self->thread;
  args->f = f;
  args->f_arg = arg;

  return pthread_create(&(self->thread), NULL, thread_chain_start_routine, (void *) args);
}

// ========= Symbolic test =========

char test_global;

void* test_write_a(void *arg)
{
#ifdef _EXPOSE_BUG_
  sleep(SHORT_DELAY);
#endif

  test_global = 'A';
  return NULL;
}

void* test_write_b(void *arg)
{
#ifdef _EXPOSE_SANITY_CHECK_
  sleep(SHORT_DELAY);
#endif

  test_global = 'B';
  return NULL;
}

int main(void)
{
#ifdef _ENABLE_CHAIN_
  struct thread_chain_t thread_chain;
  thread_chain_run(&thread_chain, test_write_a, NULL);
  thread_chain_run(&thread_chain, test_write_b, NULL);
#else
  pthread_t a_thread, b_thread;
  pthread_create(&a_thread, NULL, test_write_a, NULL);
  pthread_create(&b_thread, NULL, test_write_b, NULL);
#endif

#if defined(_EXPOSE_BUG_) || defined(_EXPOSE_SANITY_CHECK_)
  sleep(TINY_DELAY);
#endif

  char x = test_global;

#if defined(_EXPOSE_BUG_) || defined(_EXPOSE_SANITY_CHECK_)
  sleep(LONG_DELAY);
#endif

  char y = test_global;

  // Verification condition:
  //
  //  If global variable equals 'B', then it cannot change.
  //
  // It is expected that this implication
  // fails if _ENABLE_CHAIN_ is undefined.
  assert(x != 'B' || y == 'B');

#ifdef _SANITY_CHECK_
  // can be violated regardless of _ENABLE_CHAIN_
  assert(x != 'A' || y == 'A');
#endif

#ifdef _ENABLE_CHAIN_
  // the other joins happen in the chain of threads
  pthread_join(thread_chain.thread, NULL);
#else
  pthread_join(a_thread, NULL);
  pthread_join(b_thread, NULL);
#endif

  return 0;
}