Hi guys,
I am working on a video application that needs realtime performance. First I used GCD but than I saw one WWDC video where the priority decay mechanism was explained.
My application is quite simple. I have an external API call that waits for the next incoming video frame. So instead of using GCD I implemented a pthread which opt-out of the priority decay mechanism and set the priority very high as it was shown in the WWDC video.
This dispatch thread only waits for the next frame sync and starts several worker threads using conditional flags. All the worker threads also opt-out from priority decay with the same high priority like the dispatcher. They do not much, some are encoding Metal commands, others are reading some UDP data or fetching the new video frame from the hardware board.
When I used instruments I saw that all working threads has been scheduled to all available cores and blocking them all for 10ms. The UI gets unresponsive as well.
Here is a short version of the code. Maybe I understand something wrong with pthreads. So please apologize if this is a silly mistake I made here in my code. GCD would be much better to use but when I watched the WWDC video it seems that there is no way to opt-out from the priority decay problem.
void *dispatcherThread(void *params)
{
while( !forcedExit ) {
waitForNextFrame();
pthread_mutex_lock(&mutex);
needsCaptureFrame = true;
needsProcessFrame = true;
needsPlayoutFrame = true;
pthread_cond_broadcast(&condition);
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
}
void *workerThreadProcessFrame(void *params)
{
while( !forcedExit ) {
pthread_mutex_lock(&mutex);
while (!needsProcessFrame && !forcedExit)
pthread_cond_wait(&condition, &mutex);
needsProcessFrame = false;
pthread_mutex_unlock(&mutex);
if (!forcedExit) {
processFrame();
}
}
pthread_exit(NULL);
}
The C function processFrame itself is bound to a Swift function. This works pretty well. Only problem is that all worker threads block every 40ms all cores of the Mac for 10ms even when their Swift function returns in a few mikroseconds.
Here is also the code snippet how the pthreads are created.
void startThread(void * _Nullable (* _Nonnull start_routine)(void * _Nullable)) {
pthread_t thread;
pthread_attr_t attr;
int returnVal;
// create attributes with the standard values
returnVal = pthread_attr_init(&attr);
assert(!returnVal);
// set the detachstate attribute (because we don't need return values and therefor a pthread_join)
returnVal = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
assert(!returnVal);
// set the scheduling policy to round robin to avoid priority decay (this is very important!!!)
pthread_attr_setschedpolicy(&attr, SCHED_RR);
// the thread priority is set to 45 which seems to be a good value on the mac
struct sched_param param = {.sched_priority = 45};
pthread_attr_setschedparam(&attr, ¶m);
int threadError = pthread_create(&thread, &attr, start_routine, NULL);
assert(!threadError);
returnVal = pthread_attr_destroy(&attr);
assert(!returnVal);
}
I would be really happy if someone has an idea why this dispath/worker mechanism does not work or if there is also a solution with GCD avoiding the priority decay problem.