-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix simulated camera stalling #47
Fix simulated camera stalling #47
Conversation
… lock if we're about to get a frame.
fd7bd35
to
23ee9ba
Compare
…render, sleep for balance of exposure time (if any).
3340bff
to
65cbca8
Compare
65cbca8
to
369915d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the basic approach - minimizing the work inside the lock.
The approach looks like (a) set up a buffer for the rendering thread to use explicitly and (b) use the buffer in struct SimulatedCamera
as a communication channel between the rendering thread and the client thread (whatever is calling get_frame).
Allocating a buffer inside the rendering thread has some problems - you need to handle failures in a way that wasn't present before. That's certainly possible, it just needs some thought.
There's an alternative. You don't need to have the rendering buffer owned by the thread. We only have one rendering thread per SimulatedCamera instance. You could allocate an extra buffer alongside the simulated context; an extra self->im.data
with the same lifetimes etc. It looks a little like double buffering.
The upshot is that the memory allocation would happen simcam_set
which already has a defined way to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made a couple suggestions. Pretty close!
I'm realizing there's another issue here. We'd like the device api to be thread-safe, and this one's not. That should be addressed outside of this pr. I will probably make an issue.
Co-authored-by: Nathan Clack <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some more checked frees, and a suggestion
free(self->im.frame_data); | ||
|
||
self->im.frame_data = malloc(nbytes); | ||
if (!self->im.frame_data) { | ||
LOGE("Allocation of %llu bytes failed.", nbytes); | ||
lock_release(&self->im.lock); | ||
goto Error; | ||
} | ||
|
||
free(self->im.render_data); | ||
|
||
self->im.render_data = malloc(nbytes); | ||
if (!self->im.render_data) { | ||
LOGE("Allocation of %llu bytes failed.", nbytes); | ||
lock_release(&self->im.lock); | ||
goto Error; | ||
} | ||
|
||
lock_release(&self->im.lock); | ||
return Device_Ok; | ||
Error: | ||
return Device_Err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth factoring these code blocks into a function. Something like the suggestion below. One caveat is that the failure handling assumes you're inside the lock, so doing this requires modifying the top part of simcam_set
as well.
free(self->im.frame_data); | |
self->im.frame_data = malloc(nbytes); | |
if (!self->im.frame_data) { | |
LOGE("Allocation of %llu bytes failed.", nbytes); | |
lock_release(&self->im.lock); | |
goto Error; | |
} | |
free(self->im.render_data); | |
self->im.render_data = malloc(nbytes); | |
if (!self->im.render_data) { | |
LOGE("Allocation of %llu bytes failed.", nbytes); | |
lock_release(&self->im.lock); | |
goto Error; | |
} | |
lock_release(&self->im.lock); | |
return Device_Ok; | |
Error: | |
return Device_Err; | |
CHECK(checked_realloc(self->im.frame_data,nbytes); | |
CHECK(checked_realloc(self->im.render_data,nbytes); | |
int status=Device_Ok; | |
Finalize: | |
lock_release(&self->im.lock); | |
return status; | |
Error: | |
status=Device_Err; | |
goto Finalize; |
where
void* checked_realloc(void* in,size_t nbytes) {
// if `in` is NULL, realloc is identical to malloc.
void* out = realloc(in,nbytes);
EXPECT(out,"Allocation of %llu bytes failed",nbytes);
Error:
return out;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative:
static void*
checked_realloc(void* in, size_t nbytes)
{
void* out = realloc(in, nbytes);
EXPECT(out, "Allocation of %llu bytes failed.", nbytes);
Finalize:
return out;
Error:
free(in);
goto Finalize;
}
if (camera->im.frame_data) | ||
free(camera->im.frame_data); | ||
if (camera->im.render_data) | ||
free(camera->im.render_data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth searching around for other instances of these.
if (camera->im.frame_data) | |
free(camera->im.frame_data); | |
if (camera->im.render_data) | |
free(camera->im.render_data); | |
free(camera->im.frame_data); | |
free(camera->im.render_data); |
59e6c11
to
8ae31e7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm!
If you've used the simulated camera for testing, especially with more resource intensive patterns like random or radial sine, you may have noticed that unless you set your exposure time to be very large, the camera can end up stalling and the runtime hangs, sometimes clearing up on its own but sometimes requiring you to kill the process.
The reason for this is because the rendering thread locks a mutex for most of its lifetime, releasing it briefly at the bottom of a
while
loop and reacquiring it at the top. A different thread, the source thread, tries to grab on to this mutex and fails to get it, but when it retries, the rendering thread has already grabbed it again. Eventually the source thread may succeed in locking the mutex, but several (often several hundred) iterations of the render thread'swhile
loop have passed and we see that as dropped frames. When it happens over and over, performance is poor.This PR modifies the work in the rendering thread to do the following:
memcpy
the data into the camera thread's buffer.