77b13c69be0a9fb75f555edf3caa13e974ea5466
[c11tester.git] / threads.cc
1 /** @file threads.cc
2  *  @brief Thread functions.
3  */
4
5 #include <string.h>
6
7 #include <threads.h>
8 #include <mutex>
9 #include "common.h"
10 #include "threads-model.h"
11 #include "action.h"
12
13 /* global "model" object */
14 #include "model.h"
15
16 /** Allocate a stack for a new thread. */
17 static void * stack_allocate(size_t size)
18 {
19         return Thread_malloc(size);
20 }
21
22 /** Free a stack for a terminated thread. */
23 static void stack_free(void *stack)
24 {
25         Thread_free(stack);
26 }
27
28 /**
29  * @brief Get the current Thread
30  *
31  * Must be called from a user context
32  *
33  * @return The currently executing thread
34  */
35 Thread * thread_current(void)
36 {
37         ASSERT(model);
38         return model->get_current_thread();
39 }
40
41 /**
42  * Provides a startup wrapper for each thread, allowing some initial
43  * model-checking data to be recorded. This method also gets around makecontext
44  * not being 64-bit clean
45  */
46 void thread_startup()
47 {
48         Thread * curr_thread = thread_current();
49
50         /* Add dummy "start" action, just to create a first clock vector */
51         model->switch_to_master(new ModelAction(THREAD_START, std::memory_order_seq_cst, curr_thread));
52
53         /* Call the actual thread function */
54         if (curr_thread->start_routine != NULL) {
55                 curr_thread->start_routine(curr_thread->arg);
56         } else if (curr_thread->pstart_routine != NULL) {
57                 curr_thread->pstart_routine(curr_thread->arg);
58         }
59         /* Finish thread properly */
60         model->switch_to_master(new ModelAction(THREAD_FINISH, std::memory_order_seq_cst, curr_thread));
61 }
62
63 /**
64  * Create a thread context for a new thread so we can use
65  * setcontext/getcontext/swapcontext to swap it out.
66  * @return 0 on success; otherwise, non-zero error condition
67  */
68 int Thread::create_context()
69 {
70         int ret;
71
72         ret = getcontext(&context);
73         if (ret)
74                 return ret;
75
76         /* Initialize new managed context */
77         stack = stack_allocate(STACK_SIZE);
78         context.uc_stack.ss_sp = stack;
79         context.uc_stack.ss_size = STACK_SIZE;
80         context.uc_stack.ss_flags = 0;
81         context.uc_link = model->get_system_context();
82         makecontext(&context, thread_startup, 0);
83
84         return 0;
85 }
86
87 /**
88  * Swaps the current context to another thread of execution. This form switches
89  * from a user Thread to a system context.
90  * @param t Thread representing the currently-running thread. The current
91  * context is saved here.
92  * @param ctxt Context to which we will swap. Must hold a valid system context.
93  * @return Does not return, unless we return to Thread t's context. See
94  * swapcontext(3) (returns 0 for success, -1 for failure).
95  */
96 int Thread::swap(Thread *t, ucontext_t *ctxt)
97 {
98         t->set_state(THREAD_READY);
99         return model_swapcontext(&t->context, ctxt);
100 }
101
102 /**
103  * Swaps the current context to another thread of execution. This form switches
104  * from a system context to a user Thread.
105  * @param ctxt System context variable to which to save the current context.
106  * @param t Thread to which we will swap. Must hold a valid user context.
107  * @return Does not return, unless we return to the system context (ctxt). See
108  * swapcontext(3) (returns 0 for success, -1 for failure).
109  */
110 int Thread::swap(ucontext_t *ctxt, Thread *t)
111 {
112         t->set_state(THREAD_RUNNING);
113         return model_swapcontext(ctxt, &t->context);
114 }
115
116
117 /** Terminate a thread and free its stack. */
118 void Thread::complete()
119 {
120         ASSERT(!is_complete());
121         DEBUG("completed thread %d\n", id_to_int(get_id()));
122         state = THREAD_COMPLETED;
123         if (stack)
124                 stack_free(stack);
125 }
126
127 /**
128  * @brief Construct a new model-checker Thread
129  *
130  * A model-checker Thread is used for accounting purposes only. It will never
131  * have its own stack, and it should never be inserted into the Scheduler.
132  *
133  * @param tid The thread ID to assign
134  */
135 Thread::Thread(thread_id_t tid) :
136         parent(NULL),
137         creation(NULL),
138         pending(NULL),
139         start_routine(NULL),
140         arg(NULL),
141         stack(NULL),
142         user_thread(NULL),
143         id(tid),
144         state(THREAD_READY), /* Thread is always ready? */
145         last_action_val(0),
146         model_thread(true)
147 {
148         memset(&context, 0, sizeof(context));
149 }
150
151 /**
152  * Construct a new thread.
153  * @param t The thread identifier of the newly created thread.
154  * @param func The function that the thread will call.
155  * @param a The parameter to pass to this function.
156  */
157 Thread::Thread(thread_id_t tid, thrd_t *t, void (*func)(void *), void *a, Thread *parent) :
158         parent(parent),
159         creation(NULL),
160         pending(NULL),
161         start_routine(func),
162         pstart_routine(NULL),
163         arg(a),
164         user_thread(t),
165         id(tid),
166         state(THREAD_CREATED),
167         last_action_val(VALUE_NONE),
168         model_thread(false)
169 {
170         int ret;
171
172         /* Initialize state */
173         ret = create_context();
174         if (ret)
175                 model_print("Error in create_context\n");
176
177         user_thread->priv = this; // WL
178 }
179
180 /**
181  * Construct a new thread for pthread.
182  * @param t The thread identifier of the newly created thread.
183  * @param func The function that the thread will call.
184  * @param a The parameter to pass to this function.
185  */
186 Thread::Thread(thread_id_t tid, thrd_t *t, void *(*func)(void *), void *a, Thread *parent) :
187         parent(parent),
188         creation(NULL),
189         pending(NULL),
190         start_routine(NULL),
191         pstart_routine(func),
192         arg(a),
193         user_thread(t),
194         id(tid),
195         state(THREAD_CREATED),
196         last_action_val(VALUE_NONE),
197         model_thread(false)
198 {
199         int ret;
200
201         /* Initialize state */
202         ret = create_context();
203         if (ret)
204                 model_print("Error in create_context\n");
205 }
206
207
208 /** Destructor */
209 Thread::~Thread()
210 {
211         if (!is_complete())
212                 complete();
213 }
214
215 /** @return The thread_id_t corresponding to this Thread object. */
216 thread_id_t Thread::get_id() const
217 {
218         return id;
219 }
220
221 /**
222  * Set a thread's THREAD_* state (@see thread_state)
223  * @param s The state to enter
224  */
225 void Thread::set_state(thread_state s)
226 {
227         ASSERT(s == THREAD_COMPLETED || state != THREAD_COMPLETED);
228         state = s;
229 }
230
231 /**
232  * Get the Thread that this Thread is immediately waiting on
233  * @return The thread we are waiting on, if any; otherwise NULL
234  */
235 Thread * Thread::waiting_on() const
236 {
237         if (!pending)
238                 return NULL;
239
240         if (pending->get_type() == THREAD_JOIN)
241                 return pending->get_thread_operand();
242         else if (pending->get_type() == PTHREAD_JOIN)
243                 return pending->get_thread_operand();
244         else if (pending->is_lock())
245                 return (Thread *)pending->get_mutex()->get_state()->locked;
246         return NULL;
247 }
248
249 /**
250  * Check if this Thread is waiting (blocking) on a given Thread, directly or
251  * indirectly (via a chain of waiting threads)
252  *
253  * @param t The Thread on which we may be waiting
254  * @return True if we are waiting on Thread t; false otherwise
255  */
256 bool Thread::is_waiting_on(const Thread *t) const
257 {
258         Thread *wait;
259         for (wait = waiting_on(); wait != NULL; wait = wait->waiting_on())
260                 if (wait == t)
261                         return true;
262         return false;
263 }