Dear Gilles,

Thanks again for the answer...

Gilles Chanteperdrix wrote:

[...]
What happens for certain is that the access to stdout buffer, when
compiling with the -D_REENTRANT flag, is serialized with a GNU libc
POSIX mutex. This account for the consistent behaviour between GNU libc
libpthread, or Xenomai POSIX skin library. The scheduling order is the
one of the libc POSIX library, and knowing exactly what happens would
require further investigation (there may be a difference between NPTL
and linuxthreads for example).
yes, I agree that mutexes called inside stdout primitives could change the behavior of the scheduler... and that understanding what really happens in this case is probably out of the scope of the mailing list.

Working around this issue means using calls to unlocked versions of libc
functions protected with Xenomai POSIX mutexes, such as, for example,
myputs and myputchar (sufficient for Paolo example) defined as :
[...]
Ok! I tried it, and I also tried another slightly modified version of the demo, that simply replaces putchars with an unprotected array of chars (let's suppose there are no race conditions) that is printed out to stdout at the end of the game.

the results are the following, where the first part is RR and the second is FIFO (it's quite strange for me that FIFO has more contect changes than RR (?))

[EMAIL PROTECTED] xenomai-demos]# ./rt_ex_rr2
....##################################################################################L...............................................................................###############################################################################.............................................................................###############################################################################..................................................................................############################################################.......................................................... ...###L.......################...........................############.............#############.............############.............#############............#############.............################................############............#############.............#############.............############............#############.............################................#############.............############............#############............##########.............#############.............###############................############............#############.............######################

and, moreover, another strange thing is that the threads does not call any primitive...!!!
(I attach the two modified examples)


Now, your answer yields another important question: are domain
migrations inocuous from a scheduling point of view ?
Intuitively, if two tasks A and B in the same priority group are
runnable, and task A undergoes a migration from secondary to primary,
followed by a migration from primary to secondary without a suspension,
the scheduling order will be wrong.

The reason for this behaviour is that when task A migrates from
primary mode to secondary mode and will be waken up in Linux scheduler,
it should be put at the end of its priority group, so that B should
become the running task.

In short, the two successive migrations are equivalent to calling
sched_yield() from Linux scheduler point of view.

Ok, seems reasonable from the Xenomai point of view, excepts that users should be aware of this behavior since they may make assumptions on the non-preemptability at a given priority level of a particular piece of code...

bye

Paolo
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

/* for sched_param.c */
#include <sched.h> 

/* ------------------------------------------------------------ */
/* unprotected buffer to avoid migrations */

char buf[10000];
volatile char *buf_current=buf;

void myputchar(char c)
{
  *buf_current++ = c;
}

void myprintbuf(void)
{
  *buf_current=0;
  puts(buf);
}
/* ------------------------------------------------------------ */


void *low(void *arg)
{
  myputchar('L');
  return NULL;
}

void *medium(void *arg)
{
  int i,j;

  for (i=0; i<300; i++) {
    for (j=0; j<1000000; j++) ;
    myputchar(((char *)arg)[0]);
  }

  return NULL;
}

void my_create(int policy)
{
  pthread_t th1, th2, th3;
  pthread_attr_t medium_attr, low_attr;
  struct sched_param medium_policy, low_policy;

  pthread_attr_init(&medium_attr);
  pthread_attr_setschedpolicy(&medium_attr, policy);
  medium_policy.sched_priority = 2;
  pthread_attr_setschedparam(&medium_attr, &medium_policy);

  pthread_attr_init(&low_attr);
  pthread_attr_setschedpolicy(&low_attr, SCHED_FIFO);
  low_policy.sched_priority = 1;
  pthread_attr_setschedparam(&low_attr, &low_policy);

  pthread_create(&th1, &medium_attr, medium, (char *)".");
  pthread_create(&th2, &medium_attr, medium, (char *)"#");
  pthread_create(&th3, &low_attr, low, NULL);
  
  pthread_attr_destroy(&medium_attr);
  pthread_attr_destroy(&low_attr);

  pthread_join(th1, NULL);
  pthread_join(th2, NULL);
  pthread_join(th3, NULL);
  
  myputchar(' ');
  myputchar(' ');
  myputchar(' ');
}


void *high(void *arg)
{
  /* first experiment:
     - two medium priority thread scheduled with RR
     - one low priority thread scheduled with FIFO
  */
  my_create(SCHED_RR);

  /* second experiment:
     - two medium priority thread scheduled with FIFO
     - one low priority thread scheduled with FIFO
  */
  my_create(SCHED_FIFO);

  return NULL;

}

int main()
{
  pthread_t mythread;
  pthread_attr_t myattr;
  struct sched_param myparam;

  int err;
  void *returnvalue;


  /* initializes the thread attribute */
  pthread_attr_init(&myattr);
  pthread_attr_setschedpolicy(&myattr, SCHED_FIFO);
  myparam.sched_priority = 3;
  pthread_attr_setschedparam(&myattr, &myparam);

  err = pthread_create(&mythread, &myattr, high, NULL);

  if (err) {
    perror("ERROR");
    exit(1);
  }

  pthread_attr_destroy(&myattr);

  /* wait the end of the thread we just created */
  pthread_join(mythread, &returnvalue);

  myprintbuf();

  return 0;
}

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

/* for sched_param.c */
#include <sched.h> 



pthread_mutex_t xeno_stdout_lock;

int myputchar(int c)
{
    int err;

    pthread_mutex_lock(&xeno_stdout_lock);
    err = putchar_unlocked(c);
    pthread_mutex_unlock(&xeno_stdout_lock);

    return err;
}

void *low(void *arg)
{
  myputchar('L');
  return NULL;
}

void *medium(void *arg)
{
  int i,j;

  for (i=0; i<300; i++) {
    for (j=0; j<1000000; j++) ;
    myputchar(*(char *)arg);
  }

  return NULL;
}

void my_create(int policy)
{
  pthread_t th1, th2, th3;
  pthread_attr_t medium_attr, low_attr;
  struct sched_param medium_policy, low_policy;

  pthread_attr_init(&medium_attr);
  pthread_attr_setschedpolicy(&medium_attr, policy);
  medium_policy.sched_priority = 2;
  pthread_attr_setschedparam(&medium_attr, &medium_policy);

  pthread_attr_init(&low_attr);
  pthread_attr_setschedpolicy(&low_attr, SCHED_FIFO);
  low_policy.sched_priority = 1;
  pthread_attr_setschedparam(&low_attr, &low_policy);

  pthread_create(&th1, &medium_attr, medium, (char *)".");
  pthread_create(&th2, &medium_attr, medium, (char *)"#");
  pthread_create(&th3, &low_attr, low, NULL);
  
  pthread_attr_destroy(&medium_attr);
  pthread_attr_destroy(&low_attr);

  pthread_join(th1, NULL);
  pthread_join(th2, NULL);
  pthread_join(th3, NULL);
}


void *high(void *arg)
{
  /* first experiment:
     - two medium priority thread scheduled with RR
     - one low priority thread scheduled with FIFO
  */
  my_create(SCHED_RR);

  /* second experiment:
     - two medium priority thread scheduled with FIFO
     - one low priority thread scheduled with FIFO
  */
  my_create(SCHED_FIFO);

  return NULL;

}

int main()
{
  pthread_t mythread;
  pthread_attr_t myattr;
  struct sched_param myparam;

  int err;
  void *returnvalue;

  /* initializes the thread attribute */
  pthread_attr_init(&myattr);
  pthread_attr_setschedpolicy(&myattr, SCHED_FIFO);
  myparam.sched_priority = 3;
  pthread_attr_setschedparam(&myattr, &myparam);

  err = pthread_create(&mythread, &myattr, high, NULL);

  if (err) {
    perror("ERROR");
    exit(1);
  }

  pthread_attr_destroy(&myattr);

  /* wait the end of the thread we just created */
  pthread_join(mythread, &returnvalue);

  return 0;
}

Reply via email to