Rainer Orth <r...@cebitec.uni-bielefeld.de> writes:

>   All tests hang with the default -test.timeout=240.

Thanks for providing access to a Solaris system.

Right now it looks like there is a bug, or at least an incompatibility,
in the 64-bit versions of getcontext and setcontext.  It looks like
calling setcontext on the 32-bit version does not change the value of
TLS variables, which is also the case on GNU/Linux.  In the 64-bit
version, calling setcontext does change the value of TLS variables.
That is, on the 64-bit version, getcontext preserves the value of TLS
variables and setcontext restores the old value.  (Of course it's really
the pointer, not the TLS variables themselves).  This incompatibility
has to be a bug, and of course I would prefer that the incompatibility
be resolved in favor of the implementation used on GNU/Linux.

Here is a program which shows the issue.  Compile with -pthread.  With
-m32 it prints go1: tls == 2.  With -m64 it prints go1: tls == 1.  The
program is too complex for the problem, but it does show the issue.

Ian

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <pthread.h>
#include <ucontext.h>

__thread int tls;

static void die (const char *) __attribute__ ((noreturn));
static void
die (const char *msg)
{
  perror (msg);
  exit (EXIT_FAILURE);
}

static ucontext_t c1;
static void *s1[10];

static ucontext_t c2;
static void *s2[10];

static void swap (ucontext_t *, void *fs[10], ucontext_t *, void *ts[10]);

static void
swap (ucontext_t *fu, void *fs[10], ucontext_t *tu, void *ts[10])
{
  swapcontext (fu, tu);
}

static void use_buffer (char *buf) __attribute__ ((noinline));
static void
use_buffer (char *buf)
{
  buf[0] = '\0';
}

static void
down (int i, const char *msg, ucontext_t *me, void *mes[10],
      ucontext_t *other, void *others[10])
{
  char buf[10000];

  printf ("%s %d\n", msg, i);

  if (i > 0)
    {
      use_buffer (buf);
      swap (me, mes, other, others);
      down (i - 1, msg, me, mes, other, others);
    }
  else
    {
      printf ("i == 0\n");
    }
}

static void
go1 (void)
{
  printf ("go1: tls == %d\n", tls);
  down (2, "go1", &c1, s1, &c2, s2);
  pthread_exit (NULL);
}

static void
go2 (void)
{
  printf ("go2: tls == %d\n", tls);
  down (2, "go2", &c2, s2, &c1, s1);
  pthread_exit (NULL);
}

struct thread_context
{
  ucontext_t *u;
  void **s;
};

static void *
start_thread (void *context)
{
  struct thread_context *tc = (struct thread_context *) context;
  int block;

  printf ("start_thread: tls == %d\n", tls);
  tls = 2;
  block = 0;
  setcontext (tc->u);
  die ("setcontext");
  return NULL;
}

int
main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
{
  pthread_t tid;
  int err;
  size_t size;
  struct thread_context tc;
  int block;

  tls = 1;

  if (getcontext (&c1) < 0)
    die ("getcontext");

  c2 = c1;

  c1.uc_stack.ss_sp = malloc (1024 * 1024 * 1024);
  if (c1.uc_stack.ss_sp == NULL)
    die ("malloc");
  c1.uc_stack.ss_flags = 0;
  c1.uc_stack.ss_size = 1024 * 1024 * 1024;
  c1.uc_link = NULL;
  block = 0;
  makecontext (&c1, go1, 0);

  c2.uc_stack.ss_sp = malloc (1024 * 1024 * 1024);
  if (c2.uc_stack.ss_sp == NULL)
    die ("malloc");
  c2.uc_stack.ss_flags = 0;
  c2.uc_stack.ss_size = 1024 * 1024 * 1024;
  c2.uc_link = NULL;
  makecontext (&c2, go2, 0);

  tc.u = &c1;
  tc.s = &s1[0];
  err = pthread_create (&tid, NULL, start_thread, &tc);
  if (err != 0)
    {
      errno = err;
      die ("pthread_create");
    }

  err = pthread_join (tid, NULL);
  if (err != 0)
    {
      errno = err;
      die ("pthread_join");
    }

  exit (EXIT_SUCCESS);
}

Reply via email to