#ifndef	lint
static char rcs_id[] = "$Header: stack.c,v 1.4 88/05/09 16:02:47 ecc Exp $";
#endif	not lint

/*
 * threads/stack.c
 *
 * $Source: /usr0/ecc/nectar/src/cab/threads/RCS/stack.c,v $
 *
 * Allocation of thread stacks.
 */

#include <nectar_sys.h>
#include <threads.h>
#include "thread_internals.h"
#include <sys/signal.h>

PUBLIC int thread_stack_mask;
PRIVATE unsigned int thread_stack_size;


/*
 * Set up a stack segment for a thread.
 * The proc_self pointer is stored at the base.
 *
 *	--------- (high address)
 *	|	|
 *	|  ...	|
 *	|	|
 *	| stack	|
 *	|	|
 *	|  ...	|
 *	|	|
 *	--------- (stack base)
 *	| self	|
 *	--------- (low address)
 */

PRIVATE void
Set_Up_Stack(p, base)
	proc_t p;
	unsigned int base;
    BEGIN(Set_Up_Stack)
	/*
	 * Check alignment.
	 */
	ASSERT((base & thread_stack_mask) == base);
	ASSERT(((base + thread_stack_size - 1) & thread_stack_mask) == base);
	p->stack_base = (unsigned int) (base + sizeof(proc_t));
	p->stack_size = thread_stack_size - sizeof(proc_t);
	/*
	 * Store self pointer.
	 */
	*((proc_t *) base) = p;
	RET;
    END(Set_Up_Stack)


PRIVATE jmp_buf jbuf;

PRIVATE
Fault()
{
	longjmp(jbuf, 1);
	/* NOTREACHED */
}

PRIVATE int
Reference(v)
	int *v;
{
	*v = *((int *) (thread_Sp() & thread_stack_mask));
}

PUBLIC void
thread_Stack_Init(p)
	proc_t p;
    BEGIN(thread_Stack_Init)
	/*
	 * Find first power-of-two stack size that causes a fault,
	 * then use half that as the thread stack size.
	 */
	(void) signal(SIGSEGV, Fault);
	thread_stack_size = 1;
/*	for (;;) {
		int n;

		thread_stack_mask = ~(thread_stack_size - 1);
		if (setjmp(jbuf) == 0)
			(void) Reference(&n);
		else
			break;
		thread_stack_size <<= 1;
	}

	(void) signal(SIGSEGV, SIG_DFL);
	thread_stack_size >>= 1;
     */
     thread_stack_size = 32768;
	thread_stack_mask = ~(thread_stack_size - 1);
	/*
	 * Set up stack for main thread.
	 */
	Set_Up_Stack(p, (unsigned int) (thread_Sp() & thread_stack_mask));
	RET;
    END(thread_Stack_Init)


PUBLIC int
thread_Sp()
    BEGIN(thread_Sp)
	int x;

	RETURN((int) &x);
    END(thread_Sp)

/*
 * Allocate a stack segment for a thread.
 * Stacks are never deallocated.
 */
PUBLIC void
thread_Stack_Alloc(p)
	proc_t p;
    BEGIN(thread_Stack_Alloc)
	/*
	 * Use 2*size - 1 bytes to guarantee an area
	 * aligned on size boundary.
	 */
	int n = thread_stack_size;
	int b = (unsigned int) MALLOC(2*n - 1);
	unsigned int base = (b + n - 1) & ~(n - 1);

	if (b == 0)
		ERROR_HALT((msg, "*** Out of memory for C thread stacks. ***\n"));

	ASSERT(b <= base && base + n <= b + 2*n - 1);
	Set_Up_Stack(p, base);
	RET;
    END(thread_Stack_Alloc)
