1 // Copyright (c) 1999-2018 David Muse
2 // See the file COPYING for more information
3 #include <rudiments/thread.h>
4 #include <rudiments/threadmutex.h>
5 #include <rudiments/stringbuffer.h>
6 #include <rudiments/charstring.h>
7 #include <rudiments/randomnumber.h>
8 #include <rudiments/snooze.h>
9 #include <rudiments/stdio.h>
10 #include <rudiments/error.h>
11 #include "test.cpp"
12
13 struct args {
14 thread *th;
15 int32_t id;
16 };
17
18 stringbuffer output;
19 threadmutex thrm;
20 uint16_t next=1;
21
syncronize(void * args)22 void syncronize(void *args) {
23
24 struct args *a=(struct args *)args;
25
26 // We want strict alternation of the threads, where thread 0 writes 1
27 // and 3 and thread 1 writes 2 and 4. This is actually difficult to
28 // implement with mutexes. The implementation below uses a "spin-lock"
29 // strategy. Each thread grabs the mutex and checks to see if it's ok
30 // to go. If not then it unlocks and waits a random number of
31 // microseconds before checking again. Hopefully by then, the other
32 // thread will have progressed to the point of allowing the first
33 // thread to go. Actually, the wait is done before grabbing the mutex,
34 // but it's basically the same thing. Primitive, but it works.
35
36 randomnumber r;
37 r.setSeed(randomnumber::getSeed());
38
39 int32_t ms;
40
41 for (uint16_t i=0; i<10; i++) {
42 if (a->id==0) {
43 do {
44 r.generateScaledNumber(0,100,&ms);
45 snooze::microsnooze(0,ms);
46 thrm.lock();
47 if (next==1) {
48 output.append(1);
49 next=2;
50 thrm.unlock();
51 break;
52 } else {
53 thrm.unlock();
54 }
55 } while (next!=2);
56
57 do {
58 r.generateScaledNumber(0,100,&ms);
59 snooze::microsnooze(0,ms);
60 thrm.lock();
61 if (next==3) {
62 output.append(3);
63 next=4;
64 thrm.unlock();
65 break;
66 }
67 thrm.unlock();
68 } while (next!=4);
69 } else {
70 do {
71 r.generateScaledNumber(0,100,&ms);
72 snooze::microsnooze(0,ms);
73 thrm.lock();
74 if (next==2) {
75 output.append(2);
76 next=3;
77 thrm.unlock();
78 break;
79 }
80 thrm.unlock();
81 } while (next!=3);
82
83 do {
84 r.generateScaledNumber(0,100,&ms);
85 snooze::microsnooze(0,ms);
86 thrm.lock();
87 if (next==4) {
88 output.append(4);
89 next=1;
90 thrm.unlock();
91 break;
92 }
93 thrm.unlock();
94 } while (next!=1);
95 }
96 }
97 a->th->exit(&(a->id));
98 }
99
main(int argc,const char ** argv)100 int main(int argc, const char **argv) {
101
102 header("threadmutex");
103
104 if (!thread::supported()) {
105 stdoutput.printf(" not supported\n\n");
106 return 0;
107 }
108
109 // initialize threads
110 thread t[2];
111 struct args a[2];
112 for (uint16_t i=0; i<2; i++) {
113 a[i].th=&t[i];
114 a[i].id=i;
115 }
116
117 // run threads
118 for (uint16_t j=0; j<2; j++) {
119 stringbuffer title;
120 title.append("spawn ")->append(j);
121 test(title.getString(),
122 t[j].spawn((void*(*)(void*))syncronize,
123 (void *)&a[j],false));
124 }
125
126 // wait for the threads to exit
127 for (uint16_t k=0; k<2; k++) {
128
129 int32_t tstatus=-1;
130
131 stringbuffer title;
132 title.append("wait ")->append(k);
133 test(title.getString(),t[k].wait(&tstatus));
134
135 title.clear();
136 title.append("status ")->append(k);
137 test(title.getString(),tstatus==k);
138 }
139
140 // check output
141 test("output",!charstring::compare(output.getString(),
142 "1234123412341234123412341234123412341234"));
143
144 stdoutput.printf("\n");
145 }
146