Reputation: 7818
pthreads all have their own stack and share the heap. They inherit various aspects of the parent at creation time (sig masks, fpenv etc.). There seems to be little said about the relationship between the child thread and the parents stack.
So can I pass the address of something on the parent stack to a child thread via pthread_create and expect this to work? Is the child thread able to see the stack of the parent process? Does the childs stack get populated with a copy of the parent stack at creation time?
It's useful to me to know what is guaranteed by the standard and what practically works.
Upvotes: 1
Views: 3173
Reputation: 39426
So can I pass the address of something on the parent stack to a child thread via pthread_create and expect this to work?
Only if you ensure that the something on the parent stack is within scope while the child thread might access it.
For example, let's say you have a function that uses a fixed number of threads to perform some task. You can store a structure that describes the work for each thread on the parent stack, if, for example, the parent stays in the same scope (say, function), for the lifetime of the threads.
It may sound simple, but it is an easy source of bugs (kind of similar to use-after-free, except here the free part is having the variable or object referred to pass out of scope).
In general, it is much easier to allocate the structure describing the work dynamically, separately for each thread, rather than using a local variable (array). It lets you do things like
struct thread_work {
struct thread_work *next; /* Singly linked list */
pthread_t id; /* Thread ID */
/* Threads may NOT change the above, shouldn't even access them */
/* Stuff that describes the work each thread should do */
};
void cancel_threads(struct thread_work *list)
{
while (list) {
struct thread_work *curr = list;
list = list->next;
curr->next = NULL;
if (!pthread_cancel(curr->id))
pthread_join(curr->id, NULL);
free(curr);
}
}
struct work_item *create_threads(void *(*worker)(struct work_item *),
size_t count,
size_t stacksize)
{
struct work_item *list = NULL;
struct work_item *curr;
pthread_attr_t attrs;
int result;
pthread_attr_init(&attrs);
if (stacksize > 0)
pthread_attr_setstacksize(&attrs, stacksize);
while (n-->0) {
curr = malloc(sizeof *curr);
if (!curr) {
cancel_threads(list);
pthread_attr_destroy(&attrs);
errno = ENOMEM;
return NULL;
}
/* TODO: Set up work-specific fields in curr */
/* Chain items into an easily managed linked list */
curr->next = list;
list = curr;
/* Create the worker thread */
result = pthread_create(&(curr->id), &attrs,
(void *(*)(void *))worker,
curr);
if (result) {
cancel_threads(list);
pthread_attr_destroy(&attrs);
errno = result;
return NULL;
}
}
pthread_attr_destroy(&attrs);
errno = 0;
return list;
}
In general, there is rarely any sane reason why you would use a local (stack) variable rather than a dynamically allocated one as the thread parameter.
Is the child thread able to see the stack of the parent process?
Process is the entity the threads belong to. What you call "parent process" is just the initial thread. In POSIX (pthreads), the initial thread has no special properties other than it being the only one in the process, initially.
Threads can see all memory belonging to the process, including stack areas. However, it is rarely useful, because a thread accessing the stack belonging to another thread only makes sense if you ensure (using e.g. a mutex) that the thread whose stack will be examined is in the appropriate scope (running a specific function or code).
In general, you should consider stack being a churning region of memory used for internal bookkeeping, where local variables live only for the short duration they are in scope; and therefore completely unsuitable for cross-thread data sharing.
Does the childs stack get populated with a copy of the parent stack at creation time?
No. That is, you cannot assume that happens.
(While some architectures or operating systems might do that, I don't know of any current ones supporting pthreads that actually do that.)
Upvotes: 2
Reputation: 121427
So can I pass the address of something on the parent stack to a child thread via pthread_create and expect this to work? Is the child thread able to see the stack of the parent process?
Practically yes. Pretty much every pthreads implementation out there supports it. Provided the "parent" thread doesn't complete before the newly created thread which might still be using the stack address(es) from the parent, this is fine. But the standard doesn't actually require it. Passing a stack address (address of an automatic variable) from the "parent" thread to the newly created thread is said as implementation defined. So, to be absolutely sure, you'd need to dynamically allocate (with malloc
& friends) and pass it the pthread_create()
.
Does the childs stack get populated with a copy of the parent stack at creation time?
This is again implementation-defined. A new thread can be created by copying the parent stack or a separate stack is allocated. Either would be valid and there are no requirements to do it one way or other.
See the rationale section of [pthread_create()
]1 for a relevant information.
Upvotes: 2
Reputation: 24905
So can I pass the address of something on the parent stack to a child thread via pthread_create and expect this to work?
Yes. You can pass the address of something on parent thread's stack and expect it to work. But, please be aware that for any thread, the stack is active only till the thread is active. So, if your program is such that the child thread will be active even after the parent ceases to exist, you are in for trouble. Expect Undefined Behavior in that case.
Is the child thread able to see the stack of the parent process?
Child will be able to access a memory passed through pthread_create even if the memory is from parent's stack. But, if it is not passed through pthread_create, I don't think it will have access otherwise.
Does the childs stack get populated with a copy of the parent stack at creation time?
No. There is no copy of parent stack which is passed on to child.
Upvotes: 1
Reputation: 368
Given the appropriate lifetime, sure.
Pretty clearly, allowing stack-based structures to be deallocated in the parent before the other thread has finished with them is a bad idea:(
Otherwise, no actual problem. It's all one process, so no accessability issues at all. It is not uncommon, and not, in itself, bad practice.
Whether it's a good idea in any particular case, is up to you to determine and design accordingly:)
It's easiest if such practices are left to pool and/or app-lifetime threads, (ie, those never explicitly terminated/destroyed until the OS kills them as process termination). That ensures that all process threads are stopped before any memory is deallocated.
Upvotes: 1