-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsthread.c
302 lines (258 loc) · 6.99 KB
/
sthread.c
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#ifndef _POSIX_PTHREAD_SEMANTICS
#define _POSIX_PTHREAD_SEMANTICS
#endif
#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include "sthread.h"
/*
* sthread_create()
*
* Create a new thread, that will run a specified routine
* as its initial function. That function gets a specified
* int as an argument.
*
* thread -- data structure to represent the thread
* start_routine -- pointer to the function the thread
* should run
* argToStartRoutine -- argument to pass to the start_routine
*/
void sthread_create(sthread_t *thrd,
void (*start_routine)(void*),
//int arg_to_start_routine)
void *arg_to_start_routine)
{
//
// When a detached thread returns from
// its entry function, the thread will be destroyed. If we
// don't detach it, then the memory associated with the thread
// won't be cleaned up until somebody "joins" with the thread
// by calling pthread_wait().
//
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// We are using pthreads which expects to pass a pointer
// as the argument to the start routine. To make some
// examples in the book simpler, we pass an int instead.
// For safety, we need to make sure that an int fits in this
// pointer argument.
assert(sizeof(int) <= sizeof(void *));
if(pthread_create(thrd,
&attr,
(void *(*)(void *))start_routine,
//(void *)(intptr_t)arg_to_start_routine)){
arg_to_start_routine)){
perror("pthread_create failed");
exit(-1);
}
}
/*
* sthread_create_p()
*
* Create a new thread, that will run a specified routine
* as its initial function. That function gets a specified
* pointer (to anything) as an argument.
*
* thread -- data structure to represent the thread
* start_routine -- pointer to the function the thread
* should run
* argToStartRoutine -- argument to pass to the start_routine
* void* means that it can point to anything.
* start_routine() must know what type
* it really is and cast it appropriately.
*/
void sthread_create_p(sthread_t *thread,
void *(*start_routine)(void*),
void *argToStartRoutine)
{
//
// When a detached thread returns from
// its entry function, the thread will be destroyed. If we
// don't detach it, then the memory associated with the thread
// won't be cleaned up until somebody "joins" with the thread
// by calling pthread_join().
//
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if(pthread_create(thread, &attr, start_routine, argToStartRoutine)){
perror("pthread_create failed");
exit(-1);
}
}
/*
* sthread_yield()
*
* Volunteer to give up the CPU so that some other thread can run
* At some future point, the scheduler will reschedule the calling
* thread and it will return as if it had just called a (slow)
* null procedure call.
*/
void sthread_yield()
{
int err = sched_yield();
assert(err == 0);
return;
}
/*
* sthread_join()
*
* Wait for the specified thread to call sthread_exit() (if
* it hasn't already.)
*
* The value passed to sthread_exit() by the terminating thread
* is returned so that the joining
* thread can receive the "return value" of the terminating
* thread.
*
* We use casting to convert the "pointer to a pointer to
* void" into a "pointer to int", and we assume that
* an int fits into a pointer to void. See sthread_exit().
*/
long sthread_join(sthread_t thrd)
{
long ret;
void *retv;
assert(sizeof(sthread_t) == sizeof(pthread_t));
int err = pthread_join((pthread_t)thrd, &retv);
assert(err == 0);
//assert(sizeof(int) >= sizeof(void *));
ret = (long)retv;
return ret;
}
/*
* sthread_join_p()
*
* Wait for the specified thread to call sthread_exit_p() (if
* it hasn't already.)
*
* The value passed to sthread_exit() by the terminating thread
* is returned so that the joining
* thread can receive the "return value" of the terminating
* thread.
*
*/
void *sthread_join_p(sthread_t thrd)
{
void *ret;
int err = pthread_join((pthread_t)thrd, &ret);
assert(err == 0);
return ret;
}
void sthread_exit(int ret)
{
/*
* The underlying pthread_exit returns a pointer
* so that the thread can hand back a pointer to
* an arbitrary structure. For simplicity, we're
* only going to allow thread to pass back an int.
* To avoid allocating new memory (and causing a
* leak), we assume that we can fit an int
* into a pointer and cast it.
*/
assert(sizeof(int) <= sizeof(void *));
pthread_exit((void *)(intptr_t)ret);
}
void sthread_exit_p(void *ret)
{
pthread_exit(ret);
}
/*
* WARNING:
* Do not use sleep for synchronizing threads that
* should be waiting for events (using condition variables)!
* Sleep should only be used to wait for a specified amount
* of time! (If you find yourself looping on a predicate
* and calling sleep in the loop, you probably are using
* it incorrectly! We will deduct points from your grade
* if you do this!
*/
void sthread_sleep(unsigned int seconds, unsigned int nanoseconds)
{
struct timespec rqt;
assert(nanoseconds < 1000000000);
rqt.tv_sec = seconds;
rqt.tv_nsec = nanoseconds;
if(nanosleep(&rqt, NULL) != 0){
perror("sleep failed. Woke up early");
exit(-1);
}
}
void smutex_init(smutex_t *mutex)
{
if(pthread_mutex_init(mutex, NULL)){
perror("pthread_mutex_init failed");
exit(-1);
}
}
void smutex_destroy(smutex_t *mutex)
{
if(pthread_mutex_destroy(mutex)){
perror("pthread_mutex_destroy failed");
exit(-1);
}
}
void smutex_lock(smutex_t *mutex)
{
if(pthread_mutex_lock(mutex)){
perror("pthread_mutex_lock failed");
exit(-1);
}
}
void smutex_unlock(smutex_t *mutex)
{
if(pthread_mutex_unlock(mutex)){
perror("pthread_mutex_unlock failed");
exit(-1);
}
}
void scond_init(scond_t *cond)
{
if(pthread_cond_init(cond, NULL)){
perror("pthread_cond_init failed");
exit(-1);
}
}
void scond_destroy(scond_t *cond)
{
if(pthread_cond_destroy(cond)){
perror("pthread_cond_destroy failed");
exit(-1);
}
}
void scond_signal(scond_t *cond, smutex_t *muted /* NOTUSED */)
{
//
// assert(mutex is held by this thread);
//
if(pthread_cond_signal(cond)){
perror("pthread_cond_signal failed");
exit(-1);
}
}
void scond_broadcast(scond_t *cond, smutex_t *mutex /* NOTUSED */)
{
//
// assert(mutex is held by this thread);
//
if(pthread_cond_broadcast(cond)){
perror("pthread_cond_broadcast failed");
exit(-1);
}
}
void scond_wait(scond_t *cond, smutex_t *mutex)
{
//
// assert(mutex is held by this thread);
//
if(pthread_cond_wait(cond, mutex)){
perror("pthread_cond_wait failed");
exit(-1);
}
}