mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Michael Kerrisk" <mtk.manpages@gmail.com>
To: "Denys Vlasenko" <dvlasenk@redhat.com>
Cc: linux-kernel@vger.kernel.org,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Ulrich Drepper" <drepper@redhat.com>,
	"Peter Zijlstra" <a.p.zijlstra@chello.nl>,
	"Michael Kerrisk" <mtk.manpages@gmail.com>
Subject: Re: [PATCH] make setpriority POSIX compliant; introduce PRIO_THREAD extension
Date: Tue, 9 Sep 2008 17:45:15 +0200	[thread overview]
Message-ID: <517f3f820809090845o5dc772f8r2308a6c010f69561@mail.gmail.com> (raw)
In-Reply-To: <1220278355.3866.21.camel@localhost.localdomain>

Denys,

On 9/1/08, Denys Vlasenko <dvlasenk@redhat.com> wrote:
> POSIX and SUS say that setpriority(PRIO_PROCESS) should affect
>  all threads in the process:
>
>  SYNOPSIS
>        #include <sys/resource.h>
>        int getpriority(int which, id_t who);
>        int setpriority(int which, id_t who, int value);
>  DESCRIPTION
>        ...
>        The  nice value set with setpriority() shall be applied to
>        the process. If the process is multi-threaded, the nice value
>        shall affect all system scope threads in the process.
>
>  Currently, this is not the case. While PRIO_PGRP and PRIO_USER
>  do set priority to the selected group of processes, PRIO_PROCESS
>  sets priority only for the thread with tid == who.
>
>  This mostly goes unnoticed because single-threaded processes have
>  tid == pid.
>
>  However, in multi-threaded processes
>
>  setpriority(PRIO_PROCESS, getpid(), value)
>
>  sets new priority only for the thread with tid == pid, leaving
>  all other threads unaffected. This is wrong.
>
>  Attached patch changes setpriority(PRIO_PROCESS) to set priority
>  for all threads with selected pid. getpriority is changed accordingly,
>  to return the (numerical) max of all threads' priority.
>
>  In order to allow priority of individual threads to be manipulated,
>  patch adds PRIO_THREAD which acts on single thread, always.
>
>  Since there may be programs which use the fact that
>
>  setpriority(PRIO_PROCESS, tid, value)
>
>  prior to this patch was setting priority for selected thread,
>  this behavior is retained in case when tid != pid.
>
>  IOW: with PRIO_PROCESS, if pid specifies a thread group leader,
>  all threads' prios are set. Otherwise, only selected thread's priority
>  is set. (Alternative can be to just fail with ESRCH).
>
>  getpriority() is acting similarly.
>
>  Patch is run tested. I will post test program etc as a reply.

Tested-by: Michael Kerrisk <mtk.manpages@gmail.com>

Please do CC me on API changes, so that they might get documented in
the man pages.

Thanks for this work.  (I'm not sure whether or not it's a response to
my bug report, http://bugzilla.kernel.org/show_bug.cgi?id=6258 )

I tested your patch.  Most things seem to work as I would expect, but
there is one strangeness.

I would expect
setpriority(PRIO_PROCESS, getpid())
and
setpriority(PRIO_PROCESS, 0)
to have the same affect (because: which == PRIO_PRCESS, who == 0
conventionally means "the calling process").

But they do not: the latter call only changes the priority of the
calling thread.  Is this intended?

My test program is at the end of this mail.  Here are two tests
demonstrating the behavior in each of the above cases:

$ sudo ./thread_nice_test p p
root's password:
main(): TID = 6574, PID = 6574
main(): initial nice value is 0

Thread 1: TID = 6578
Thread 2: TID = 6579
Loop 0
    process nice value is 0
    main(): nice value is 0
    Thread 1: nice value is 0
    Thread 2: nice value is 0
Thread 1: setpriority() succeeded (0, 6574)
Loop 1
    process nice value is -10
    main(): nice value is -10
    Thread 1: nice value is -10
    Thread 2: nice value is -10
Loop 2
    process nice value is -10
    main(): nice value is -10
    Thread 1: nice value is -10
    Thread 2: nice value is -10


$ sudo ./thread_nice_test p 0
main(): TID = 6590, PID = 6590
main(): initial nice value is 0

Thread 1: TID = 6594
Thread 2: TID = 6595
Loop 0
    process nice value is 0
    main(): nice value is 0
    Thread 1: nice value is 0
    Thread 2: nice value is 0
Thread 1: setpriority() succeeded (0, 0)
Loop 1
    process nice value is -10
    main(): nice value is 0
    Thread 1: nice value is -10
    Thread 2: nice value is 0
Loop 2
    process nice value is -10
    main(): nice value is 0
    Thread 1: nice value is -10
    Thread 2: nice value is 0

=====

/* thread_nice_test.c

   Copyright 2008, Linux Foundation, written by Michael Kerrisk
        <mtk.manpages@gmail.com>
   Licensed under GNU GPL version 2 or later

   Creates two subthreads.
   The first thread does a setpriority() with arguments
   based on command-line arguments.
   The main() program loops, using getpriority() to monitor
   priority of all threads, and the process as a whole.
*/
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define PRIO_THREAD 3

#define errExit(msg)            { perror(msg); exit(EXIT_FAILURE); }

#define errExitEN(en, msg)      { errno = en; perror(msg); \
                                  exit(EXIT_FAILURE); }

#define fatal(msg)              { fprintf(stderr, "%s\n", msg); \
                                  exit(EXIT_FAILURE); }

#define NEW_NICE -10

static pid_t
gettid(void)
{
    return syscall(SYS_gettid);
}

static pid_t t1_tid, t2_tid, main_tid;
static char cwhich, cwho;

static void *
tf1_nice(void *x)
{
    int which, who;

    t1_tid = gettid();
    printf("Thread 1: TID = %ld\n", (long) gettid());

    sleep(1);

    which = (cwhich == 'p') ? PRIO_PROCESS :
            (cwhich == 't') ? PRIO_THREAD :
            9999;       /* Invalid value */
    who =   (cwho == '0') ? 0 :
            (cwho == 't') ? gettid() :
            (cwho == 'p') ? getpid() :
            (cwho == 'm') ? main_tid :
            -1;                 /* Invalid value */
    if (setpriority(which, who, NEW_NICE) == -1)
        errExit("setpriority");

    printf("Thread 1: setpriority() succeeded (%d, %d)\n", which, who);

    sleep(10);
    printf("Thread 1 terminating\n");
    return NULL;
}

static void *
tf2_nice(void *x)
{
    t2_tid = gettid();
    printf("Thread 2: TID = %ld\n", (long) gettid());

    sleep (10);

    printf("Thread 2 terminating\n");
    return NULL;
}


int
main(int argc, char *argv[])
{
    pthread_t t1, t2;
    int s;
    int nval;
    int j;

    if (argc != 3) {
        fprintf(stderr, "Usage: %s <which> <who>\n", argv[0]);
        fprintf(stderr, "which is\n");
        fprintf(stderr, "    p   PRIO_PROCESS\n");
        fprintf(stderr, "    t   PRIO_THREAD\n");
        fprintf(stderr, "who is:\n");
        fprintf(stderr, "    0   0\n");
        fprintf(stderr, "    t   gettid()\n");
        fprintf(stderr, "    p   getpid()\n");
        fprintf(stderr, "    m   TID of main()\n");
        exit(EXIT_FAILURE);
    }

    main_tid = gettid();
    cwhich = argv[1][0];
    cwho = argv[2][0];

    printf("main(): TID = %ld, PID = %ld\n",
            (long) gettid(), (long) getpid());

    errno = 0;
    nval = getpriority(PRIO_PROCESS, gettid());
    if (nval == -1 && errno != 0)
        errExit("getpriority");
    printf("main(): initial nice value is %d\n", nval);


    printf("\n");

    s = pthread_create(&t1, NULL, tf1_nice, (void *) 1);
    if (s != 0)
        errExitEN(s, "pthread_create");

    s = pthread_create(&t2, NULL, tf2_nice, (void *) 2);
    if (s != 0)
        errExitEN(s, "pthread_create");

    usleep(200000);  /* Allow time for subthreads to set global vars */

    for (j = 0; j < 3; j++) {
        printf("Loop %d\n", j);
        errno = 0;
        nval = getpriority(PRIO_PROCESS, gettid());
        if (nval == -1 && errno != 0)
            errExit("getpriority");
        printf("    process nice value is %d\n", nval);

        errno = 0;
        nval = getpriority(PRIO_THREAD, gettid());
        if (nval == -1 && errno != 0)
            errExit("getpriority");
        printf("    main(): nice value is %d\n", nval);

        errno = 0;
        nval = getpriority(PRIO_THREAD, t1_tid);
        if (nval == -1 && errno != 0)
            errExit("getpriority");
        printf("    Thread 1: nice value is %d\n", nval);

        errno = 0;
        nval = getpriority(PRIO_THREAD, t2_tid);
        if (nval == -1 && errno != 0)
            errExit("getpriority");
        printf("    Thread 2: nice value is %d\n", nval);

        sleep(2);
    }

    exit(EXIT_SUCCESS);
} /* main */

  parent reply	other threads:[~2008-09-09 15:45 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-09-01 14:12 Denys Vlasenko
2008-09-01 14:17 ` Denys Vlasenko
2008-09-01 14:42 ` Denys Vlasenko
2008-09-01 15:08   ` Peter Zijlstra
2008-09-01 15:19     ` Peter Zijlstra
2008-09-01 15:20     ` Denys Vlasenko
2008-09-01 17:58     ` Ulrich Drepper
2008-09-09 15:45 ` Michael Kerrisk [this message]
2008-09-09 16:42   ` Chris Friesen
2008-09-09 18:45     ` Michael Kerrisk
2008-09-10  9:28   ` Denys Vlasenko
2008-09-10 11:57     ` Michael Kerrisk
2008-09-10 12:08 ` Christoph Hellwig

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=517f3f820809090845o5dc772f8r2308a6c010f69561@mail.gmail.com \
    --to=mtk.manpages@gmail.com \
    --cc=a.p.zijlstra@chello.nl \
    --cc=akpm@linux-foundation.org \
    --cc=drepper@redhat.com \
    --cc=dvlasenk@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®