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