From 325e08065dc04c8c8d17ce4ca0bea1688f7f781c Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Thu, 9 Sep 2010 11:48:31 +0000 Subject: [PATCH] Fix priority dropping Lowering priority was wrong. Some bugs: 1) You don't lower the priority by setting the scheduler policy to some of the real-time ones (SCHED_RR or SCHER_FIFO). If you do so, you are actually increasing the priority of the workers and your main thread you be preempted and stalled until the workers complete their job. Fortunately this will only happen if your programming is running as root, as normal users (without CAP_SYS_NICE) are unable to set priority to real-time values. 2) setpriority() and getpriority() are not part of pthread and you can't use the id returned by pthread. Manpage explicitly says so on pthread_self(3): "The thread ID returned by pthread_self() is not the same thing as the kernel thread ID returned by a call to gettid(2)." Since glibc does not have a gettid, here we are using syscall(SYS_gettid) This patch was tested with the program below. Compile and run: $ gcc p_hello2.c -o p_hello2 -lpthread $ ./p_hello2 10 You'll see that the main thread remains with its priority and threads created by the main thread change their own niceness. #include #include #include #include #include #include #include #include #include /* Lower priority of current thread. * * It's used by worker threads so they use up "bg cpu" as it was really intended * to work. If current thread is running with real-time priority, we decrease * our priority by 5. This is done in a portable way. Otherwise we are * running with SCHED_OTHER policy and there's no portable way to set the nice * level on current thread. In Linux, it does work and it's the only one that is * implemented. */ static void _ecore_thread_pri_drop(void) { struct sched_param param; int pol, prio, ret; pid_t tid; pthread_t pthread_id; pthread_id = pthread_self(); ret = pthread_getschedparam(pthread_id, &pol, ¶m); if (ret) { fprintf(stderr, "Unable to query sched parameters\n"); return; } if (pol == SCHED_RR || pol == SCHED_FIFO) { prio = sched_get_priority_max(pol); param.sched_priority += 5; if (prio > 0 && param.sched_priority > prio) param.sched_priority = prio; pthread_setschedparam(pthread_id, pol, ¶m); } #ifdef __linux__ else { tid = syscall(SYS_gettid); errno = 0; prio = getpriority(PRIO_PROCESS, tid); if (errno == 0) { prio += 5; if (prio > 19) prio = 19; setpriority(PRIO_PROCESS, tid, prio); } } #endif } /* * p_hello.c -- a hello program (in pthread) */ #define MAX_THREAD 1000 typedef struct { int id; } parm; void *hello(void *arg) { parm *p=(parm *)arg; pid_t tid; int prio; tid = syscall(SYS_gettid); printf("[%d] Hello from node %d\n", tid, p->id); pthread_yield(); printf("[%d] tid=%lu\n", tid); _ecore_thread_pri_drop(); prio = getpriority(PRIO_PROCESS, tid); printf("[%d] New nice value: %d\n", tid, prio); return (NULL); } void main(int argc, char* argv[]) { int n,i; pthread_t *threads; pthread_attr_t pthread_custom_attr; parm *p; pid_t tid; int prio; if (argc != 2) { printf ("Usage: %s n\n where n is no. of threads\n",argv[0]); exit(1); } n=atoi(argv[1]); if ((n < 1) || (n > MAX_THREAD)) { printf ("The no of thread should between 1 and %d.\n",MAX_THREAD); exit(1); } threads = (pthread_t *)malloc(n * sizeof(*threads)); pthread_attr_init(&pthread_custom_attr); p = (parm *)malloc(n * sizeof(parm)); /* Start up thread */ tid = syscall(SYS_gettid); for (i=0; i # include # include +# include # include # endif #endif @@ -222,6 +223,58 @@ _ecore_long_job(Ecore_Pipe *end_pipe, pthread_t thread) } } +/* Lower priority of current thread. + * + * It's used by worker threads so they use up "bg cpu" as it was really intended + * to work. If current thread is running with real-time priority, we decrease + * our priority by 5. This is done in a portable way. Otherwise we are + * running with SCHED_OTHER policy and there's no portable way to set the nice + * level on current thread. In Linux, it does work and it's the only one that is + * implemented. + */ +static void +_ecore_thread_pri_drop(void) +{ + struct sched_param param; + int pol, prio, ret; + pid_t tid; + pthread_t pthread_id; + + pthread_id = pthread_self(); + ret = pthread_getschedparam(pthread_id, &pol, ¶m); + if (ret) + { + ERR("Unable to query sched parameters"); + return; + } + + if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO)) + { + prio = sched_get_priority_max(pol); + param.sched_priority += 5; + if (prio > 0 && param.sched_priority > prio) + param.sched_priority = prio; + + pthread_setschedparam(pthread_id, pol, ¶m); + } +#ifdef __linux__ + else + { + tid = syscall(SYS_gettid); + errno = 0; + prio = getpriority(PRIO_PROCESS, tid); + if (errno == 0) + { + prio += 5; + if (prio > 19) + prio = 19; + + setpriority(PRIO_PROCESS, tid, prio); + } + } +#endif +} + static void * _ecore_direct_worker(Ecore_Pthread_Worker *work) { @@ -229,6 +282,7 @@ _ecore_direct_worker(Ecore_Pthread_Worker *work) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + _ecore_thread_pri_drop(); pth = malloc(sizeof (Ecore_Pthread_Data)); if (!pth) return NULL; @@ -276,6 +330,7 @@ _ecore_thread_worker(Ecore_Pthread_Data *pth) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + _ecore_thread_pri_drop(); pthread_mutex_lock(&_ecore_pending_job_threads_mutex); _ecore_thread_count++; @@ -321,35 +376,6 @@ _ecore_thread_worker(Ecore_Pthread_Data *pth) return pth->p; } -static void -_ecore_thread_pri_drop(pthread_t th) -{ - /* lower priority of worker threads so they use up "bg cpu" - * as it was really intended to work */ -#ifdef __linux__ - struct sched_param param; - - /* yes - this may fail if not root. there is no portable way to do - * this so try - if it fails. meh. nothnig to be done. - */ - memset(¶m, 0, sizeof(param)); - param.sched_priority = sched_get_priority_max(SCHED_RR); - if (pthread_setschedparam(th, SCHED_RR, ¶m) != 0) - { - int newp; - - errno = 0; - newp = getpriority(PRIO_PROCESS, 0); - if (errno == 0) - { - newp += 5; - if (newp > 19) newp = 19; - setpriority(PRIO_PROCESS, th, newp); - } - } -#endif -} - #endif void @@ -476,10 +502,7 @@ ecore_thread_run(Ecore_Cb func_blocking, if (!pth->p) goto on_error; if (pthread_create(&pth->thread, NULL, (void *) _ecore_thread_worker, pth) == 0) - { - _ecore_thread_pri_drop(pth->thread); - return (Ecore_Thread *) work; - } + return (Ecore_Thread *) work; on_error: if (pth) @@ -645,10 +668,7 @@ EAPI Ecore_Thread *ecore_long_run(Ecore_Thread_Heavy_Cb func_heavy, pthread_t t; if (pthread_create(&t, NULL, (void *) _ecore_direct_worker, worker) == 0) - { - _ecore_thread_pri_drop(t); - return (Ecore_Thread *) worker; - } + return (Ecore_Thread *) worker; } pthread_mutex_lock(&_ecore_pending_job_threads_mutex); @@ -670,10 +690,7 @@ EAPI Ecore_Thread *ecore_long_run(Ecore_Thread_Heavy_Cb func_heavy, if (!pth->p) goto on_error; if (pthread_create(&pth->thread, NULL, (void *) _ecore_thread_worker, pth) == 0) - { - _ecore_thread_pri_drop(pth->thread); - return (Ecore_Thread *) worker; - } + return (Ecore_Thread *) worker; on_error: if (pth)