source: doc/theses/thierry_delisle_PhD/code/readQ_example/proto-gui/main.cpp@ 29185fc

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 29185fc was 29185fc, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

Working towards allowing different thread frameworks to be picked from the command line

  • Property mode set to 100644
File size: 6.8 KB
Line 
1#include "thrdlib/thread.hpp"
2
3#include <cassert>
4
5#include <algorithm>
6#include <atomic>
7#include <iostream>
8#include <memory>
9#include <vector>
10
11#include <getopt.h>
12using thrdlib::thread_t;
13
14
15//--------------------
16// Constants
17unsigned nframes;
18unsigned fsize;
19unsigned nproduce;
20
21//--------------------
22// Frame management
23
24class Frame {
25 static const thread_t reset;
26 static const thread_t set;
27 std::atomic<thread_t> rdy_state = { reset };
28 std::atomic<thread_t> rnd_state = { set };
29public:
30 unsigned number;
31 std::unique_ptr<unsigned char[]> data;
32
33private:
34 inline bool wait( thread_t self, std::atomic<thread_t> & state, std::atomic<thread_t> & other ) {
35 bool ret;
36 while(true) {
37 thread_t expected = state;
38 if( expected == set ) { ret = false; goto END; }
39 assert( expected == reset );
40 if( std::atomic_compare_exchange_strong( &state, &expected, self) ) {
41 thrdlib::park( self );
42 ret = true;
43 goto END;
44 }
45 }
46 END:
47 assert( state == set );
48 assert( other != set );
49 state = reset;
50 return ret;
51 }
52
53 inline bool publish( std::atomic<thread_t> & state ) {
54 thread_t got = std::atomic_exchange( &state, set );
55 assert( got != set );
56
57 if( got == reset ) return false;
58
59 thrdlib::unpark( got );
60 return true;
61 }
62
63public:
64 inline bool wait_rendered( thread_t self ) {
65 return wait( self, rnd_state, rdy_state );
66 }
67
68 inline bool wait_ready ( thread_t self ) {
69 return wait( self, rdy_state, rnd_state );
70 }
71
72 inline bool publish() {
73 return publish( rdy_state );
74 }
75
76 inline bool release() {
77 return publish( rnd_state );
78 }
79};
80
81const thread_t Frame::reset = nullptr;
82const thread_t Frame::set = reinterpret_cast<thread_t>(1);
83
84std::unique_ptr<Frame[]> frames;
85volatile unsigned last_produced = 0;
86
87//--------------------
88// Threads
89thread_t volatile the_stats_thread = nullptr;
90
91inline void fence(void) {
92 std::atomic_thread_fence(std::memory_order_seq_cst);
93}
94
95struct {
96 struct {
97 volatile unsigned long long parks = 0;
98 volatile unsigned long long unparks = 0;
99 } sim;
100 struct {
101 volatile unsigned long long parks = 0;
102 volatile unsigned long long unparks = 0;
103 } rend;
104
105 struct {
106 volatile unsigned long long ran = 0;
107 volatile unsigned long long saw = 0;
108 } stats;
109} thrd_stats;
110
111void Stats( thread_t self ) {
112 the_stats_thread = self;
113 fence();
114 thrdlib::park( self );
115
116 std::vector<bool> seen;
117 seen.resize(nproduce, false);
118
119 while(last_produced < nproduce) {
120 thrdlib::yield();
121 thrd_stats.stats.ran++;
122 if( last_produced > 0 ) seen.at(last_produced - 1) = true;
123 }
124
125 thrd_stats.stats.saw = std::count(seen.begin(), seen.end(), true);
126}
127
128void Simulator( thread_t self ) {
129 for(unsigned i = 0; i < nproduce; i++) {
130 auto & frame = frames[i % nframes];
131 // Wait for the frames to be rendered
132 if( frame.wait_rendered( self ) ) {
133 thrd_stats.sim.parks++;
134 }
135
136 // Write the frame information
137 frame.number = i;
138 for( unsigned x = 0; x < fsize; x++ ) {
139 frame.data[x] = i;
140 }
141 std::cout << "Simulated " << i << std::endl;
142 last_produced = i+1;
143
144 // Publish it
145 if( frame.publish() ) {
146 thrd_stats.sim.unparks++;
147 }
148 }
149}
150
151void Renderer( thread_t self ) {
152 thrdlib::unpark( the_stats_thread );
153 for(unsigned i = 0; i < nproduce; i++) {
154 auto & frame = frames[i % nframes];
155 // Wait for the frames to be ready
156 if( frame.wait_ready( self ) ) {
157 thrd_stats.rend.parks++;
158 }
159
160 // Render the frame
161 unsigned total = 0;
162 for( unsigned x = 0; x < fsize; x++ ) {
163 total += frame.data[x];
164 }
165
166 std::cout << "Rendered " << i << std::endl;
167 assert(total == i * fsize);
168
169 // Release
170 if( frame.release() ) {
171 thrd_stats.rend.unparks++;
172 }
173 }
174
175}
176
177
178
179int main(int argc, char * argv[]) {
180 nframes = 3;
181 fsize = 1000;
182 nproduce = 60;
183
184 for(;;) {
185 static struct option options[] = {
186 {"buff", required_argument, 0, 'b'},
187 {"nprod", required_argument, 0, 'p'},
188 {"fsize", required_argument, 0, 'f'},
189 {0, 0, 0, 0}
190 };
191
192 int idx = 0;
193 int opt = getopt_long(argc, argv, "b:p:f:", options, &idx);
194
195 std::string arg = optarg ? optarg : "";
196 size_t len = 0;
197 switch(opt) {
198 // Exit Case
199 case -1:
200 /* paranoid */ assert(optind <= argc);
201 goto run;
202 case 'b':
203 try {
204 nframes = std::stoul(optarg, &len);
205 if(nframes == 0 || len != arg.size()) { throw std::invalid_argument(""); }
206 } catch(std::invalid_argument &) {
207 std::cerr << "Number of buffered frames must be at least 1, was" << arg << std::endl;
208 goto usage;
209 }
210 break;
211 case 'p':
212 try {
213 nproduce = std::stoul(optarg, &len);
214 if(nproduce == 0 || len != arg.size()) { throw std::invalid_argument(""); }
215 } catch(std::invalid_argument &) {
216 std::cerr << "Number of produced frames must be at least 1, was" << arg << std::endl;
217 goto usage;
218 }
219 break;
220 case 'f':
221 try {
222 fsize = std::stoul(optarg, &len);
223 if(fsize == 0 || len != arg.size()) { throw std::invalid_argument(""); }
224 } catch(std::invalid_argument &) {
225 std::cerr << "Size of produced frames must be at least 1, was" << arg << std::endl;
226 goto usage;
227 }
228 break;
229 // Other cases
230 default: /* ? */
231 std::cerr << opt << std::endl;
232 usage:
233 std::cerr << "Usage: " << argv[0] << " [options]" << std::endl;
234 std::cerr << std::endl;
235 std::cerr << " -b, --buff=COUNT Number of frames to buffer" << std::endl;
236 std::cerr << " -p, --nprod=COUNT Number of frames to produce" << std::endl;
237 std::cerr << " -f, --fsize=SIZE Size of each frame in bytes" << std::endl;
238 std::exit(1);
239 }
240 }
241 run:
242
243 frames.reset(new Frame[nframes]);
244 for(unsigned i = 0; i < nframes; i++) {
245 frames[i].number = 0;
246 frames[i].data.reset(new unsigned char[fsize]);
247 }
248 std::cout << "Created frames of " << fsize << " bytes" << std::endl;
249 std::cout << "(Buffering " << nframes << ")" << std::endl;
250
251 thrdlib::init( "fibre", 2 );
252
253 thread_t stats = thrdlib::create( Stats );
254 std::cout << "Created Stats Thread" << std::endl;
255 while( the_stats_thread == nullptr ) thrdlib::yield();
256
257 std::cout << "Creating Main Threads" << std::endl;
258 thread_t renderer = thrdlib::create( Renderer );
259 thread_t simulator = thrdlib::create( Simulator );
260
261 std::cout << "Running" << std::endl;
262
263 thrdlib::join( simulator );
264 thrdlib::join( renderer );
265 thrdlib::join( stats );
266
267 thrdlib::clean();
268
269 std::cout << "----------" << std::endl;
270 std::cout << "# Parks" << std::endl;
271 std::cout << " Renderer park: " << thrd_stats. sim. parks << std::endl;
272 std::cout << " Renderer unpark: " << thrd_stats. sim.unparks << std::endl;
273 std::cout << " Simulator park: " << thrd_stats.rend. parks << std::endl;
274 std::cout << " Simulator unpark: " << thrd_stats.rend.unparks << std::endl;
275
276 std::cout << "Stats thread" << std::endl;
277 std::cout << " Ran : " << thrd_stats.stats.ran << " times" << std::endl;
278 std::cout << " Saw : " << thrd_stats.stats.saw << " (" << ((100.f * thrd_stats.stats.saw) / nproduce) << "%)" << std::endl;
279}
Note: See TracBrowser for help on using the repository browser.