Application-managed pthreads stacks leaking

I'm trying to use application-managed stacks, i.e. allocating the stack myself and passing it to the pthread creation attributes through pthread_setstack -function. My problem is that the memory allocated for the stack seems to leak after the thread terminates.

Only information I have been able to find about this is in System Interfaces Chapter 2 section 2.9.8 "Use of Application-Managed Thread Stacks". It states that the application grants to the implementation permanent ownership over the memory region, and that in particular, the region of memory cannot be freed, nor can it be later specified as the stack for another thread.

I've tried to free the stack manually after making sure the thread in question has terminated (by joining to it), which seems to kind of work since the virtual size of the application decreases. However, when I allocate a new stack for a new thread from the heap and the memory previous used for another threads stack is reused, the application crashes at pthread_create.

I'm trying to accomplish (what I'm sure is non-portable) thread stack monitoring by periodically checking the thread stack memory regions for a high water mark. If someone knows another way to do this, I'd be glad to drop the whole application-manager stacks thing.

Has anyone experienced similar problems or alternatively successfully used application-managed stacks?

I'm using NPTL 4.2.

Thanks in advance,
Lasse

You could take a look at the codebase for Valgrind as a start for monitoring the threads stack.
You should be able to free the malloc'd stack memory on pthread_exit, set the stack pointer to NULL and reuse.

/*header defs*/
typedef struct custom_thread {
        int cr_return;
	int thread_stack_size;
	pthread_t tid;
	pthread_attr_t attr;
	void *newstack;
} CTHREAD;
extern CTHREAD *init_custom_thread(int, int (*)(void *));
extern void destroy_custom_thread(CTHREAD *);

/*from shared library*/
extern CTHREAD *init_custom_thread(int sz, int (*teststackalloc)(void *arg)) {
CTHREAD *ret;

               if ( (ret = malloc(sizeof(*ret))) == NULL) {return NULL;}
               ret->thread_stack_size = sz;
               if ( (ret->newstack = malloc(sz)) == NULL) {free(ret); return NULL;}
               if (teststackalloc!= NULL) {
                    if (teststackalloc(ret->newstack) != 0) {free(ret->newstack); free(ret); return NULL;}
               }
               pthread_attr_init(&ret->attr);
               ret->cr_return = pthread_attr_setstack(&ret->attr,ret->newstack,ret->thread_stack_size);
               return ret;
}

extern void destroy_custom_thread(CTHREAD *arg) {

                                  if (arg->newstack != NULL) {free(arg->newstack); arg->newstack = NULL;}
                                  free(arg);
                                  arg = NULL;
}

/*test program*/

#include "custom_stack.h"
#include <limits.h>

void *hello_world(void *arg) {
                             
                  printf("Hello world from thread id %d.\n",pthread_self());
                  pthread_exit(NULL);
}

void dothread_work(CTHREAD *new_id) {
               pthread_create(&new_id->tid,&new_id->attr,hello_world,NULL);
               pthread_join(new_id->tid,NULL);
               destroy_custom_thread(new_id);
}
                
int main(void) {
int i = 0; 
CTHREAD *new_id;

               new_id = init_custom_thread(PTHREAD_STACK_MIN * 2, NULL);
               if (new_id == NULL) {return 1;}
               for (i = 0 ; i < 100; i++) {dothread_work(new_id); new_id = init_custom_thread(PTHREAD_STACK_MIN * 2, NULL);}
               dothread_work(new_id);
               return 0;
}

All works as expected.

Thanks for the reply.

I could've sworn that I was doing the exact same thing, but I must have missed something, since the piece of code you provided works perfectly.

Anyways, thanks for you help. It was much appreciated.