|
Intro to threading and cpu free waiting for signals |
Edgar Reynaldo
Major Reynaldo
May 2007
|
I need a way to wait in another thread for allegro's event queue to pop up with a timer event without disturbing the main thread but also at the same time being able to wake the main thread up if it receives an event. Can anyone give me a simple walkthrough of how these things are done? Do I need allegro's threads, do I need mutexes (I'm guessing I probably do)?, do I need pthreads, or what? I think pthreads had something like a 'wait for signal' function but I can't remember what it was called now. Anybody have some knowledge to share about these things? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Arthur Kalliokoski
Second in Command
February 2005
|
Edgar Reynaldo said: I need a way to wait in another thread for allegro's event queue to pop up with a timer event without disturbing the main thread but also at the same time being able to wake the main thread up if it receives an event. I was under the impression that's what the message loop did by default, as long as there was an al_wait_for_event() to cause it to idle when no events occurred. I assume you want the main thread to sleep, since you specify "wake the main thread up". They all watch too much MSNBC... they get ideas. |
l j
Member #10,584
January 2009
|
I think you might want to use 2 event queues, one for each thread.
|
Thomas Fjellstrom
Member #476
June 2000
|
Yup, just send messages back and forth. -- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
Maybe I didn't explain well. I'm trying to implement my own event sources and I need the timer thread to sleep until the timer goes off, and then emit the timer event. As it is now I have to poll for the timer event in al_wait_for_event, but I want it asynchronous so I can just add the timer events to a message queue w/o disturbing the main thread. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Thomas Fjellstrom
Member #476
June 2000
|
That sounds less clear than your first question... any thread can setup an event queue, and event sources, including timers. They can then wait on that queue. Any thread can add events to a queue, which will only wake up other threads that are `al_wait_for_event`ing on that queue. Edgar Reynaldo said: As it is now I have to poll for the timer event in al_wait_for_event `al_wait_for_event` actually sleeps. It does not poll. -- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
I'm trying to accomplish what allegro does with its event queues, that is it wakes up the thread when the signal goes off. Maybe I have to post example code to show what I mean. I'm not at home though so can't post it atm. I have a timer class. I am trying to decide whether to make it an eagle event source or to have my event handler subscribe allegro's timer event source to its queue. If I make my timer an event source then I need to emit an event whenever the timer goes off. That means I have to call al_wait_for_event (and use up one thread) to poll for the timer event using the private allegro event queue and timer. That's what I meant by polling. So I want to create a thread that will call al_wait_for_event to wait for a timer. I want that thread to access an event queue and post messages to it, what I call an event handler. So do I need a mutex to protect the queue while I am asynchronously adding an event to it? Does allegro's event queues use mutexes? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Thomas Fjellstrom
Member #476
June 2000
|
Event queues are thread safe. You don't need to worry about locking around them afaik. -- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
No, do I need a mutex for my eagle event queues, not for an allegro event queue. I want one thread to wait on the timer, and then emit events whenever it goes off. However the event queue I want it to post messages to is on a different thread, hence the question about the mutex. I don't want to access the event queue at the same time I am adding an event to it. Do I need a mutex to keep my eagle event queues thread safe? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Thomas Fjellstrom
Member #476
June 2000
|
Oh, it depends on how you implemented your event queues. You can implement lockless queue algorithms, but I'm doubting you did so. so yes, you probably do want locking around access to your queue's internals. There are also other locking primitives you could use, like condvars or semaphores. -- |
bamccaig
Member #7,536
July 2006
|
Sounds a bit like you're reinventing the square wheel that is Allegro's event queue. Is there a reason that you can't just use Allegro's event queues as suggested? -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
l j
Member #10,584
January 2009
|
bamccaig said: Sounds a bit like you're reinventing the square wheel that is Allegro's event queue. Is there a reason that you can't just use Allegro's event queues as suggested? I believe his library is supposed to support multiple back-ends and I think he doesn't want the core to have to rely on allegro.
|
Edgar Reynaldo
Major Reynaldo
May 2007
|
bamccaig said: Sounds a bit like you're reinventing the square wheel that is Allegro's event queue. Is there a reason that you can't just use Allegro's event queues as suggested? Well actually yes I am a little bit. I just need mostly the same functionality with a small wrapper over it for abstraction and portabilities sake. I need my Timer class to be an event source but I don't want to poll for things. I want to post an event right away. Which means I need another thread to work alongside the main one, so I suppose I should wrap a Thread too. It's just adding another link in the chain of signals I guess. No biggie. taron said: I believe his library is supposed to support multiple back-ends and I think he doesn't want the core to have to rely on allegro. You are correct sir. You can look up my latest code on sourceforge through svn : svn checkout "svn://svn.code.sf.net/p/eagle5gui/code/trunk eagle5gui-code"
or just the url (click on copy link location) It's building now and I'm working on tests to make sure everything works. Adventurous souls welcome, but the only docs I have are the headers. There is a simple example program that displays your input and the frames per second. The build system is currently CB Projects, so you would have to adjust your linking and search directories to match whatever version of A5 you have installed. I think what I need are semaphores. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Arthur Kalliokoski
Second in Command
February 2005
|
Edgar Reynaldo said: I think what I need are semaphores. AFAIK, semaphores might not work, what if each CPU has differing values in their respective caches? Of course, if you accessed this semaphore only through a mutex, it'd work. They all watch too much MSNBC... they get ideas. |
Edgar Reynaldo
Major Reynaldo
May 2007
|
What do you mean, semaphores might not work? They're not reliable ways to sleep and wake up a thread? Forgot to post example code : 1
2
3#include "RecordInputTestMain.hpp"
4
5#include "Eagle/backends/Allegro5Backend.hpp"
6
7#include <sstream>
8using std::ostringstream;
9#include <string>
10using std::string;
11
12int RecordInputTestMain(int argc , char** argv) {
13
14// if (!SendOutputToFile("RecordInputTestMain.txt" , "Record input test\n\n" , false)) {return 1;}
15
16 Allegro5System sys;
17
18 int start_state = EAGLE_FULL_SETUP;
19 if (sys.Initialize(start_state) != start_state) {
20 return -1;
21 }
22
23 int SCREEN_WIDTH = 800;
24 int SCREEN_HEIGHT = 600;
25 EagleGraphicsContext* win = sys.CreateGraphicsContext(SCREEN_WIDTH , SCREEN_HEIGHT , EAGLE_WINDOWED | EAGLE_OPENGL);
26
27 if (!win->Valid()) {
28 return -2;
29 }
30
31 EagleFont* font40 = win->LoadFont("Data/fonts/verdana.ttf" , 40 , 0 , VIDEO_IMAGE);
32 EagleFont* font20 = win->LoadFont("Data/fonts/verdana.ttf" , 20 , 0 , VIDEO_IMAGE);
33
34 if (!font40->Valid() || !font20->Valid()) {
35 return -3;
36 }
37
38 int count = 0;
39 int scount = 0;
40 bool quit = false;
41 bool redraw = true;
42 int fps = 0;
43 int framecount = 0;
44
45 Input press;
46 Input held;
47 InputGroup ig;
48
49 sys.GetSystemTimer()->Start();
50
51 do {
52
53 if (redraw) {
54
55 ostringstream oss;
56 oss.str("");
57 ig.ShowLogic(oss);
58
59
60 win->Clear(EagleColor(0,0,0));
61 win->DrawTextString(font40 , StringPrintF("FPS : %i" , fps) , 40 , 40 , EagleColor(0,255,255));
62 win->DrawTextString(font40 , StringPrintF("framecount = %i" , framecount + 1) , 40 , 80 , EagleColor(0,255,255));
63 win->DrawTextString(font20 , oss.str() , 400 , 300 , EagleColor(255,255,255) , DRAW_TEXT_CENTER , DRAW_TEXT_TOP);
64 win->FlipDisplay();
65 ++framecount;
66 redraw = false;
67 }
68
69 do {
70 EagleEvent ee = sys.UpdateSystemState();
71
72 if (AnyInputPressed(&press)) {
73 if (ModifierHeld(&held)) {
74 ig = press && held;
75 }
76 else {
77 ig = InputGroup(press);
78 }
79 redraw = true;
80 }
81
82 if (ee.type == EAGLE_EVENT_TIMER && true/*ee.timer.source == sys.GetSystemTimer()*/) {
83 count += 1;
84 if (count == 60) {
85 scount += 1;
86 count = 0;
87 fps = framecount;
88 framecount = 0;
89 }
90// fps = count;
91 redraw = true;
92 }
93 else if (ee.type == EAGLE_EVENT_KEY_DOWN) {
94 if (ee.keyboard.keycode == EAGLE_KEY_ESCAPE) {
95 quit = true;
96 break;
97 }
98 }
99 if (ee.type == EAGLE_EVENT_DISPLAY_CLOSE) {
100 quit = true;
101 break;
102 }
103
104 } while (!sys.UpToDate());
105 } while (!quit);
106
107
108 return 0;
109}
My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Arthur Kalliokoski
Second in Command
February 2005
|
I'm wrong, Wikipedia says you're right. I was thinking you just meant ordinary booleans (like a semaphore flag, on a mailbox or a railroad crossing). They all watch too much MSNBC... they get ideas. |
Edgar Reynaldo
Major Reynaldo
May 2007
|
yeah, I meant a pthread semaphore. I may just wrap Allegro's Thread routines. They seem solid enough to write a driver around. Edit Allegro5Timer.cpp 1#include "Eagle/backends/Allegro5/Allegro5EventHandler.hpp"
2#include "Eagle/backends/Allegro5/Allegro5Timer.hpp"
3#include "Eagle/backends/Allegro5/Allegro5Threads.hpp"
4
5
6
7
8enum EAGLE_TIMER_MESSAGE_TYPE {
9 EAGLE_MESSAGE_START_TIMER = 1,
10 EAGLE_MESSAGE_STOP_TIMER = 2,
11 EAGLE_MESSAGE_CLOSE_TIMER = 3
12};
13
14
15
16void* TimerProcess(EagleThread* ethread , void* etimer) {
17 EagleTimer* eagle_timer = (EagleTimer*)etimer;
18 Allegro5Timer* eagle_a5_timer = dynamic_cast<Allegro5Timer*>(eagle_timer);
19 EAGLE_ASSERT(eagle_a5_timer);
20
21 ALLEGRO_TIMER* timer = eagle_a5_timer->AllegroTimer();
22 ALLEGRO_EVENT_QUEUE* queue = eagle_a5_timer->AllegroEventQueue();
23
24 EAGLE_ASSERT(timer); 25 EAGLE_ASSERT(queue); 26
27 int counter = 0;
28 bool close = false;
29 while (!ethread->ShouldStop() && !close) {
30 ALLEGRO_EVENT ev;
32 OutputLog() << "Event " << ev.type << std::endl;
33 if (ev.type == ALLEGRO_EVENT_TIMER && ev.timer.source == (ALLEGRO_TIMER*)eagle_a5_timer->Source()) {
34 ++counter;
35 eagle_a5_timer->Tick(al_get_time());
36 }
37 else if (ev.type == EAGLE_EVENT_USER_START) {
38 switch (ev.user.data1) {
39 case EAGLE_MESSAGE_START_TIMER :
40 al_start_timer(timer);
41 break;
42 case EAGLE_MESSAGE_STOP_TIMER :
43 al_stop_timer(timer);
44 break;
45 case EAGLE_MESSAGE_CLOSE_TIMER :
46 close = true;
47 break;
48 default : EAGLE_ASSERT(0);
49 break;
50 }
51 }
52 }
53 return (void*)counter;
54}
55
56
57
58void Allegro5Timer::SendTimerProcessMessage(int message) {
59 ALLEGRO_EVENT ev;
60 ev.type = EAGLE_EVENT_USER_START;
61 ev.user.data1 = message;
63}
64
65
66
67Allegro5Timer::Allegro5Timer() :
68 EagleTimer(),
69 timer(0),
70 queue(0),
71 queue_lock(0),
72 event_source(),
73 ethread(0)
74{
76}
77
78
79
80Allegro5Timer::~Allegro5Timer() {
81 Destroy();
82}
83
84
85
86bool Allegro5Timer::Create(double seconds_per_tick) {
87 EAGLE_ASSERT(seconds_per_tick > 0.0);
88
89 Destroy();
90
91 timer = al_create_timer(seconds_per_tick);
92 queue = al_create_event_queue();
93 ethread = new Allegro5Thread();
94
95 EAGLE_ASSERT(timer);
96 EAGLE_ASSERT(queue);
97
98 if (queue && timer) {
99 spt = seconds_per_tick;
100 previous_ticks = current_ticks = al_get_timer_count(timer);
101 al_register_event_source(queue , al_get_timer_event_source(timer));
102 al_register_event_source(queue , &event_source);
103 /// MUST create TimerProcess thread AFTER registering event sources or it will wait forever
104 ethread->Create(TimerProcess , this); 105 if (ethread->Valid()) {
106 ethread->Start();
107 return true;
108 }
109 }
110
111 if (!queue) {
112 OutputLog() << "Allegro5Timer::Create - Could not create an Allegro 5 Timer - Couldn't create an ALLEGRO_EVENT_QUEUE." << std::endl;
113 }
114 if (!timer) {
115 OutputLog() << "Allegro5Timer::Create - Could not create an Allegro 5 Timer - Couldn't create an ALLEGRO_TIMER." << std::endl;
116 }
117 if (!ethread->Valid()) {
118 OutputLog() << "Allegro5Timer::Create - ethread is not valid." << std::endl;
119 }
120
121 // The queue or the timer failed to be created
122 Destroy();
123 return false;
124}
125
126
127
128void Allegro5Timer::Destroy() {
129 if (ethread && running) {
130 SendTimerProcessMessage(EAGLE_MESSAGE_CLOSE_TIMER);
131 ethread->Join();
132 }
133 if (queue) {
134 al_destroy_event_queue(queue);
135 queue = 0;
136 }
137 if (timer) {
138 al_destroy_timer(timer);
139 timer = 0;
140 }
141 if (ethread) {
142 delete ethread;
143 ethread = 0;
144 }
145 spt = 0.0;
146}
147
148
149
150void Allegro5Timer::Start() {
151 SendTimerProcessMessage(EAGLE_MESSAGE_START_TIMER);
152}
153
154
155
156void Allegro5Timer::Stop() {
157 SendTimerProcessMessage(EAGLE_MESSAGE_STOP_TIMER);
158}
159
160
161
162void Allegro5Timer::WaitForTick() {
163 if (queue && timer) {
164 do {
165 ALLEGRO_EVENT e;
166 al_wait_for_event(queue , &e);
167 if (e.type == ALLEGRO_EVENT_TIMER) {
168 Tick(al_get_time());
169 break;
170 }
171 } while (true);
172 }
173 return;
174}
175
176
177
178void* Allegro5Timer::Source() {
179 return timer;
180}
181
182
183
184void Allegro5Timer::RefreshTimer() {
185 if (queue && timer) {
186 ALLEGRO_EVENT ev;
187 while (al_get_next_event(queue , &ev)) {
188 if (ev.type == ALLEGRO_EVENT_TIMER) {
189 Tick(al_get_time());
190 }
191 }
192 }
193}
194
195
196
197bool Allegro5Timer::Valid() {
198 return timer && queue && ethread->Valid();
199}
200
201
202
203long long int Allegro5Timer::Count() {
204 if (timer) {return al_get_timer_count(timer);}
205 return -1;
206}
207
208
209
210void Allegro5Timer::RegisterTimerInput(EagleEventHandler* event_handler) {
211/*
212 EAGLE_ASSERT(event_handler);
213 Allegro5EventHandler* a5_event_handler = dynamic_cast<Allegro5EventHandler*>(event_handler);
214 EAGLE_ASSERT(allegro_handler);
215
216 ALLEGRO_EVENT_QUEUE* allegro_queue = a5_event_handler->AllegroQueue();
217 EAGLE_ASSERT(allegro_queue);
218 al_register_event_source(allegro_queue , al_get_timer_event_source(timer));
219*/
220 // so we don't have timers registered to queues and eagle timers registered as event sources at the same time
221 SubscribeListener(event_handler);/// TODO Convert to an event source
222}
I marked all the lines I thought were important. Does anybody see anything wrong with my logic? I make a new thread in Allegro5Timer::Create, make an allegro timer and queue, and make sure they're both valid and register the event sources and then and only then do I create and start the thread that runs TimerProcess, where I keep getting the two marked assertion failures that the timer and queue are both null. I don't get it. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
|