Mandelbrot: Improve thread handling code.
* Application is now much more stable and less likely to crash * Almost all communication to render threads is done through semaphores. Change-Id: I7858cdb4a2b83ef4bfe44588cebef01bd3304346 Reviewed-on: https://review.haiku-os.org/730 Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
parent
36f94d9e39
commit
e920ab3cb6
@ -33,13 +33,13 @@ FractalEngine::FractalEngine(BHandler* parent, BLooper* looper)
|
||||
fRenderBuffer(NULL),
|
||||
fRenderBufferLen(0),
|
||||
fMessenger(parent, looper),
|
||||
fThreadsRendering(0),
|
||||
fIterations(1024),
|
||||
fColorset(Colorset_Royal)
|
||||
{
|
||||
fDoSet = &FractalEngine::DoSet_Mandelbrot;
|
||||
|
||||
fRenderSem = create_sem(0, "RenderSem");
|
||||
fRenderStoppedSem = create_sem(0,"RenderStopped");
|
||||
|
||||
system_info info;
|
||||
get_system_info(&info);
|
||||
@ -49,8 +49,6 @@ FractalEngine::FractalEngine(BHandler* parent, BLooper* looper)
|
||||
fThreadCount = MAX_RENDER_THREADS;
|
||||
|
||||
for (uint8 i = 0; i < fThreadCount; i++) {
|
||||
fRestartRenderThread[i] = false;
|
||||
fRenderThreadRunning[i] = false;
|
||||
fRenderThreads[i] = spawn_thread(&FractalEngine::RenderThread,
|
||||
BString().SetToFormat("RenderThread%d", i).String(),
|
||||
B_NORMAL_PRIORITY, this);
|
||||
@ -95,6 +93,14 @@ void FractalEngine::MessageReceived(BMessage* msg)
|
||||
break;
|
||||
|
||||
case MSG_RESIZE: {
|
||||
TRACE("Got MSG_RESIZE threads rendering\n");
|
||||
if (fStopRender) {
|
||||
// Will be true throughout this whole handler. Set false at the end
|
||||
TRACE("Breaking out of MSG_RESIZE handler\n");
|
||||
break;
|
||||
}
|
||||
StopRender();
|
||||
|
||||
delete fRenderBuffer;
|
||||
fRenderBuffer = NULL;
|
||||
|
||||
@ -102,17 +108,20 @@ void FractalEngine::MessageReceived(BMessage* msg)
|
||||
fHeight = msg->GetUInt16("height", 240);
|
||||
|
||||
TRACE("Creating new buffer. width %u height %u\n", fWidth, fHeight);
|
||||
|
||||
fRenderBufferLen = fWidth * fHeight * 3;
|
||||
fRenderBuffer = new uint8[fRenderBufferLen];
|
||||
memset(fRenderBuffer, 0, fRenderBufferLen);
|
||||
|
||||
BMessage message(MSG_BUFFER_CREATED);
|
||||
fMessenger.SendMessage(&message);
|
||||
|
||||
fStopRender = false;
|
||||
break;
|
||||
}
|
||||
case MSG_RENDER: {
|
||||
TRACE("Got MSG_RENDER. fThreadsRendering = %u\n",fThreadsRendering);
|
||||
TRACE("Got MSG_RENDER.\n");
|
||||
StopRender();
|
||||
fStopRender = false;
|
||||
Render(msg->GetDouble("locationX", 0), msg->GetDouble("locationY", 0),
|
||||
msg->GetDouble("size", 0.005));
|
||||
break;
|
||||
@ -121,9 +130,9 @@ void FractalEngine::MessageReceived(BMessage* msg)
|
||||
TRACE("Got MSG_THREAD_RENDER_COMPLETE for thread %d\n",
|
||||
msg->GetUInt8("thread", 0));
|
||||
|
||||
fThreadsRendering--;
|
||||
TRACE("Set fRendering = %d\n", fThreadsRendering);
|
||||
if (!fThreadsRendering) {
|
||||
int32 threadsStopped;
|
||||
get_sem_count(fRenderStoppedSem, &threadsStopped);
|
||||
if (threadsStopped == fThreadCount) {
|
||||
TRACE("Done rendering!\n");
|
||||
BMessage message(MSG_RENDER_COMPLETE);
|
||||
fMessenger.SendMessage(&message);
|
||||
@ -145,9 +154,32 @@ void FractalEngine::WriteToBitmap(BBitmap* bitmap)
|
||||
B_RGB24);
|
||||
}
|
||||
|
||||
void FractalEngine::StopRender()
|
||||
{
|
||||
if (fRenderStopped)
|
||||
return;
|
||||
fRenderStopped = true;
|
||||
// true if another call to StopRender() won't work properly because
|
||||
// the fRenderStoppedSem semaphores have already been acquired.
|
||||
TRACE("Stopping render...\n");
|
||||
fStopRender = true;
|
||||
for (uint i = 0; i<fThreadCount; i++)
|
||||
acquire_sem(fRenderStoppedSem);
|
||||
|
||||
TRACE("Render stopped.\n");
|
||||
// note that fStopRender is NOT set to false at the end here.
|
||||
// This is to allow the message handlers to use this variable to
|
||||
// block duplication of stuff.
|
||||
}
|
||||
|
||||
|
||||
void FractalEngine::Render(double locationX, double locationY, double size)
|
||||
{
|
||||
fRenderStopped = false;
|
||||
// This means that future Render calls will need to call stop render
|
||||
if (fStopRender)
|
||||
debugger("Error: Render shouldn't be called while fStopRender = true\n");
|
||||
|
||||
fLocationX = locationX;
|
||||
fLocationY = locationY;
|
||||
fSize = size;
|
||||
@ -155,18 +187,10 @@ void FractalEngine::Render(double locationX, double locationY, double size)
|
||||
TRACE("Location: %g;%g;%g\n", fLocationX, fLocationY, fSize);
|
||||
|
||||
for (uint8 i = 0; i < fThreadCount; i++) {
|
||||
if (fRenderThreadRunning[i])
|
||||
fRestartRenderThread[i] = true;
|
||||
else
|
||||
release_sem(fRenderSem);
|
||||
fRenderThreadRunning[i] = true;
|
||||
release_sem(fRenderSem);
|
||||
}
|
||||
|
||||
fThreadsRendering = fThreadCount;
|
||||
TRACE("Set fRendering = %d\n", fThreadsRendering);
|
||||
}
|
||||
|
||||
|
||||
status_t FractalEngine::RenderThread(void* data)
|
||||
{
|
||||
FractalEngine* engine = static_cast<FractalEngine*>(data);
|
||||
@ -179,19 +203,12 @@ status_t FractalEngine::RenderThread(void* data)
|
||||
}
|
||||
}
|
||||
|
||||
bool restarting = false;
|
||||
while (true) {
|
||||
TRACE("Thread %d starting another render pass\n", threadNum);
|
||||
|
||||
if (!restarting) {
|
||||
TRACE("Thread %d awaiting semaphore...\n", threadNum);
|
||||
acquire_sem(engine->fRenderSem);
|
||||
TRACE("Thread %d got semaphore!\n", threadNum);
|
||||
} else {
|
||||
TRACE("Thread %d setting restart = false\n", threadNum);
|
||||
restarting = false;
|
||||
}
|
||||
release_sem(engine->fRenderStoppedSem);
|
||||
|
||||
TRACE("Thread %d awaiting semaphore...\n", threadNum);
|
||||
acquire_sem(engine->fRenderSem);
|
||||
TRACE("Thread %d got semaphore!\n", threadNum);
|
||||
|
||||
uint16 width = engine->fWidth;
|
||||
uint16 height = engine->fHeight;
|
||||
@ -213,17 +230,13 @@ status_t FractalEngine::RenderThread(void* data)
|
||||
// halfHeight-(halfHeight-1)-1 = 0
|
||||
}
|
||||
|
||||
if (engine->fRestartRenderThread[threadNum]) {
|
||||
TRACE("Thread %d Got restart message\n", threadNum);
|
||||
|
||||
engine->fRestartRenderThread[threadNum] = false;
|
||||
restarting = true;
|
||||
if (engine->fStopRender) {
|
||||
TRACE("Thread %d stopping\n", threadNum);
|
||||
break; // Restart the loop to update width, height, etc.
|
||||
}
|
||||
}
|
||||
|
||||
if (!restarting) {
|
||||
engine->fRenderThreadRunning[threadNum] = false;
|
||||
if (!engine->fStopRender) {
|
||||
BMessage message(FractalEngine::MSG_THREAD_RENDER_COMPLETE);
|
||||
message.AddUInt8("thread", threadNum);
|
||||
engine->PostMessage(&message);
|
||||
|
@ -50,10 +50,10 @@ private:
|
||||
uint8 fThreadCount;
|
||||
thread_id fRenderThreads[MAX_RENDER_THREADS];
|
||||
sem_id fRenderSem;
|
||||
sem_id fRenderStoppedSem;
|
||||
|
||||
uint8 fThreadsRendering;
|
||||
bool fRestartRenderThread[MAX_RENDER_THREADS];
|
||||
bool fRenderThreadRunning[MAX_RENDER_THREADS];
|
||||
bool fStopRender;
|
||||
bool fRenderStopped;
|
||||
|
||||
double fLocationX;
|
||||
double fLocationY;
|
||||
@ -66,6 +66,7 @@ private:
|
||||
int32 (FractalEngine::*fDoSet)(double real, double imaginary);
|
||||
|
||||
void Render(double locationX, double locationY, double size);
|
||||
void StopRender();
|
||||
static status_t RenderThread(void* data);
|
||||
void RenderPixel(uint32 x, uint32 y);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user