The appended program, when run on a stock Red Hat 5.2 system, produces
incorrect output after allocating approximately 6 megs of stack space.
Output appears after the program.  (Don't forget "-lpthread" if you
compile it yourself.)

The problem is that the LinuxThreads library sets its internal notion
of STACK_SIZE to a constant 2M.  It subtracts twice STACK_SIZE from
the initial value of the stack pointer and rounds down to a multiple
of STACK_SIZE to figure out a nominal "bottom-of-stack" value
for the initial thread.

It uses this bottom-of-stack value for two things.

First, it uses it to figure out which thread is calling into the
library (e.g., for thread_self() and for thread specific data); this
is the problem illustrated by the test case below.  Once esp goes
below the bottom-of-stack value, the library no longer knows that the
calling thread is the initial thread.  (A stock Red Hat system has a
stack size rlimit of 8M.)

Second, it uses the initial thread bottom-of-stack as the top of an
array of contiguously allocated STACK_SIZE chunks, which serve as the
stacks for additional threads.  That is, the second, third,
etc. threads will have stacks which are contiguously allocated below
the nominal bottom-of-stack of the initial thread.  If the stack size
rlimit is greater than 2M, this means each thread will have a stack
overlapping that of some other thread.

This is a Bad Thing.  Not only does it limit each thread's stack usage
to a compiled-in constant of 2M (a value neither detectable nor
settable through getrlimit/setrlimit), it also prevents the kernel
from generating a signal on stack overflow for most threads.  And
while that may be fine for C, it creates a royal pain for people (like
us) who are trying to implement a safe language on top of
LinuxThreads.

I think two things need to happen to fix these problems.

First, STACK_SIZE in LinuxThreads needs to default to a value greater
than the kernel-determined hard rlimit (under Linux, I believe this is
8M).  I realize this will reduce the number of threads you can create,
so perhaps STACK_SIZE should be settable at run-time as part of an
advanced LinuxThreads-specific interface.  Either way, having some
lower value hard-wired in is truly irritating.

Second, LinuxThreads should leave some amount of unallocated,
inaccessible memory between the stacks of the various threads.
Whether that is 4K or 8M, I don't care; it just needs to be an
advertised value so that we can write proper stack probes when
allocating stack space.  (Yes, in JIT-compiled code we could just test
the esp every time, but some of our code is written in C, and all we
can do there is place bounds on the local variable space used.)
Without these sentinels, it is a real pain to guarantee that a user
cannot violate the safety of the language.

Thanks for your time.

 - Pat

======================================================================

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

void * volatile x;

int
main(int argc, char *argv[])
{
  pthread_key_t k;
  size_t s;

  pthread_key_create(&k, 0);
  pthread_setspecific(k, (void *)0x100);

  for (s = 0; s < 80; s++)
    {
      printf("%ldK %p\n", s * 100L, pthread_getspecific(k));
      x = alloca(100 * 1024);
    }

  return 0;
}

======================================================================

0K 0x100
100K 0x100
200K 0x100
300K 0x100
400K 0x100
500K 0x100
600K 0x100
700K 0x100
800K 0x100
900K 0x100
1000K 0x100
1100K 0x100
1200K 0x100
1300K 0x100
1400K 0x100
1500K 0x100
1600K 0x100
1700K 0x100
1800K 0x100
1900K 0x100
2000K 0x100
2100K 0x100
2200K 0x100
2300K 0x100
2400K 0x100
2500K 0x100
2600K 0x100
2700K 0x100
2800K 0x100
2900K 0x100
3000K 0x100
3100K 0x100
3200K 0x100
3300K 0x100
3400K 0x100
3500K 0x100
3600K 0x100
3700K 0x100
3800K 0x100
3900K 0x100
4000K 0x100
4100K 0x100
4200K 0x100
4300K 0x100
4400K 0x100
4500K 0x100
4600K 0x100
4700K 0x100
4800K 0x100
4900K 0x100
5000K 0x100
5100K 0x100
5200K 0x100
5300K 0x100
5400K 0x100
5500K 0x100
5600K 0x100
5700K 0x100
5800K 0x100
5900K 0x100
6000K 0x100
6100K 0x100
6200K (nil)
6300K (nil)
6400K (nil)
6500K (nil)
6600K (nil)
6700K (nil)
6800K (nil)
6900K (nil)
7000K (nil)
7100K (nil)
7200K (nil)
7300K (nil)
7400K (nil)
7500K (nil)
7600K (nil)
7700K (nil)
7800K (nil)
7900K (nil)

Reply via email to