fork(): Defer signals and lock the heaps while _kern_fork().
* Also defer signals while registering fork hooks. * While malloc provides fork heap hooks which lock the heaps and unlock/reinit, malloc_debug provides empty hooks. * Ideas suggested by Ingo, patch reviewed by him. Thanks a lot! * Also call fork parent hooks on failure. * Solve locks-up when combining multithreading and process forking, should help with #13111.
This commit is contained in:
@@ -36,6 +36,9 @@ void __init_env(const struct user_space_program_args *args);
|
|||||||
void __init_env_post_heap(void);
|
void __init_env_post_heap(void);
|
||||||
status_t __init_heap(void);
|
status_t __init_heap(void);
|
||||||
void __heap_terminate_after(void);
|
void __heap_terminate_after(void);
|
||||||
|
void __heap_before_fork(void);
|
||||||
|
void __heap_after_fork_child(void);
|
||||||
|
void __heap_after_fork_parent(void);
|
||||||
|
|
||||||
void __init_time(addr_t commPageTable);
|
void __init_time(addr_t commPageTable);
|
||||||
void __arch_init_time(struct real_time_data *data, bool setDefaults);
|
void __arch_init_time(struct real_time_data *data, bool setDefaults);
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ static size_t sFreeHeapSize, sHeapAreaSize;
|
|||||||
static free_chunk *sFreeChunks;
|
static free_chunk *sFreeChunks;
|
||||||
|
|
||||||
|
|
||||||
static void
|
void
|
||||||
init_after_fork(void)
|
__init_after_fork(void)
|
||||||
{
|
{
|
||||||
// find the heap area
|
// find the heap area
|
||||||
sHeapArea = area_for((void*)sFreeHeapBase);
|
sHeapArea = area_for((void*)sFreeHeapBase);
|
||||||
@@ -110,13 +110,6 @@ __init_heap(void)
|
|||||||
|
|
||||||
hoardLockInit(sHeapLock, "heap");
|
hoardLockInit(sHeapLock, "heap");
|
||||||
|
|
||||||
atfork(&init_after_fork);
|
|
||||||
// Note: Needs malloc(). Hence we need to be fully initialized.
|
|
||||||
// TODO: We should actually also install a hook that is called before
|
|
||||||
// fork() is being executed. In a multithreaded app it would need to
|
|
||||||
// acquire *all* allocator locks, so that we don't fork() an
|
|
||||||
// inconsistent state.
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,8 +132,7 @@ hoardHeap::hoardHeap(void)
|
|||||||
, _magic(HEAP_MAGIC)
|
, _magic(HEAP_MAGIC)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// Initialize the per-heap lock.
|
initLock();
|
||||||
hoardLockInit(_lock, "hoard heap");
|
|
||||||
|
|
||||||
for (int i = 0; i < SUPERBLOCK_FULLNESS_GROUP; i++) {
|
for (int i = 0; i < SUPERBLOCK_FULLNESS_GROUP; i++) {
|
||||||
for (int j = 0; j < SIZE_CLASSES; j++) {
|
for (int j = 0; j < SIZE_CLASSES; j++) {
|
||||||
|
|||||||
@@ -115,6 +115,9 @@ class hoardHeap {
|
|||||||
// Unlock this heap.
|
// Unlock this heap.
|
||||||
inline void unlock(void);
|
inline void unlock(void);
|
||||||
|
|
||||||
|
// Init this heap lock.
|
||||||
|
inline void initLock(void);
|
||||||
|
|
||||||
// Set our index number (which heap we are).
|
// Set our index number (which heap we are).
|
||||||
inline void setIndex(int i);
|
inline void setIndex(int i);
|
||||||
|
|
||||||
@@ -444,6 +447,14 @@ hoardHeap::unlock(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
hoardHeap::initLock(void)
|
||||||
|
{
|
||||||
|
// Initialize the per-heap lock.
|
||||||
|
hoardLockInit(_lock, "hoard heap");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
hoardHeap::align(const size_t sz)
|
hoardHeap::align(const size_t sz)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class processHeap : public hoardHeap {
|
|||||||
// Get a thread heap index.
|
// Get a thread heap index.
|
||||||
inline int getHeapIndex(void);
|
inline int getHeapIndex(void);
|
||||||
|
|
||||||
|
// Get thread heap max.
|
||||||
|
inline int getMaxThreadHeaps(void);
|
||||||
|
|
||||||
// Get the thread heap with index i.
|
// Get the thread heap with index i.
|
||||||
inline HEAPTYPE & getHeap(int i);
|
inline HEAPTYPE & getHeap(int i);
|
||||||
|
|
||||||
@@ -199,6 +202,15 @@ processHeap::getHeapIndex(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Return the maximum number of heaps.
|
||||||
|
|
||||||
|
int
|
||||||
|
processHeap::getMaxThreadHeaps(void)
|
||||||
|
{
|
||||||
|
return fMaxThreadHeaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
superblock *
|
||||||
processHeap::acquire(const int sizeclass, hoardHeap * dest)
|
processHeap::acquire(const int sizeclass, hoardHeap * dest)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -256,6 +256,35 @@ getAllocator(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
__heap_before_fork(void)
|
||||||
|
{
|
||||||
|
static processHeap *pHeap = getAllocator();
|
||||||
|
for (int i = 0; i < pHeap->getMaxThreadHeaps(); i++)
|
||||||
|
pHeap->getHeap(i).lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __init_after_fork(void);
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
__heap_after_fork_child(void)
|
||||||
|
{
|
||||||
|
__init_after_fork();
|
||||||
|
static processHeap *pHeap = getAllocator();
|
||||||
|
for (int i = 0; i < pHeap->getMaxThreadHeaps(); i++)
|
||||||
|
pHeap->getHeap(i).initLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
__heap_after_fork_parent(void)
|
||||||
|
{
|
||||||
|
static processHeap *pHeap = getAllocator();
|
||||||
|
for (int i = 0; i < pHeap->getMaxThreadHeaps(); i++)
|
||||||
|
pHeap->getHeap(i).unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - public functions
|
// #pragma mark - public functions
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,24 @@ __heap_terminate_after()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
__heap_before_fork(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
__heap_after_fork_child(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
__heap_after_fork_parent(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - Public API
|
// #pragma mark - Public API
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <pthread_private.h>
|
#include <pthread_private.h>
|
||||||
#include <runtime_loader.h>
|
#include <runtime_loader.h>
|
||||||
#include <syscalls.h>
|
#include <syscalls.h>
|
||||||
|
#include <user_thread.h>
|
||||||
|
|
||||||
|
|
||||||
typedef struct fork_hook {
|
typedef struct fork_hook {
|
||||||
@@ -105,9 +106,13 @@ call_fork_hooks(fork_hook *hook)
|
|||||||
status_t
|
status_t
|
||||||
__register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
|
__register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
|
||||||
{
|
{
|
||||||
|
defer_signals();
|
||||||
|
|
||||||
status_t status = mutex_lock(&sForkLock);
|
status_t status = mutex_lock(&sForkLock);
|
||||||
if (status != B_OK)
|
if (status != B_OK) {
|
||||||
|
undefer_signals();
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
if (prepare)
|
if (prepare)
|
||||||
status = add_fork_hook(&sPrepareHooks, NULL, prepare);
|
status = add_fork_hook(&sPrepareHooks, NULL, prepare);
|
||||||
@@ -119,6 +124,9 @@ __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(voi
|
|||||||
status = add_fork_hook(&sChildHooks, &sLastChildHook, child);
|
status = add_fork_hook(&sChildHooks, &sLastChildHook, child);
|
||||||
|
|
||||||
mutex_unlock(&sForkLock);
|
mutex_unlock(&sForkLock);
|
||||||
|
|
||||||
|
undefer_signals();
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,21 +137,19 @@ fork(void)
|
|||||||
thread_id thread;
|
thread_id thread;
|
||||||
status_t status;
|
status_t status;
|
||||||
|
|
||||||
|
defer_signals();
|
||||||
|
|
||||||
status = mutex_lock(&sForkLock);
|
status = mutex_lock(&sForkLock);
|
||||||
if (status != B_OK)
|
if (status != B_OK) {
|
||||||
|
undefer_signals();
|
||||||
return status;
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// call preparation hooks
|
// call preparation hooks
|
||||||
call_fork_hooks(sPrepareHooks);
|
call_fork_hooks(sPrepareHooks);
|
||||||
|
__heap_before_fork();
|
||||||
|
|
||||||
thread = _kern_fork();
|
thread = _kern_fork();
|
||||||
if (thread < 0) {
|
|
||||||
// something went wrong
|
|
||||||
mutex_unlock(&sForkLock);
|
|
||||||
__set_errno(thread);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread == 0) {
|
if (thread == 0) {
|
||||||
// we are the child
|
// we are the child
|
||||||
// ToDo: initialize child
|
// ToDo: initialize child
|
||||||
@@ -155,15 +161,25 @@ fork(void)
|
|||||||
// process we should make sure that it is in a consistent state when
|
// process we should make sure that it is in a consistent state when
|
||||||
// calling the kernel.
|
// calling the kernel.
|
||||||
__gRuntimeLoader->reinit_after_fork();
|
__gRuntimeLoader->reinit_after_fork();
|
||||||
|
__heap_after_fork_child();
|
||||||
__reinit_pwd_backend_after_fork();
|
__reinit_pwd_backend_after_fork();
|
||||||
|
|
||||||
call_fork_hooks(sChildHooks);
|
call_fork_hooks(sChildHooks);
|
||||||
} else {
|
} else {
|
||||||
// we are the parent
|
// we are the parent
|
||||||
|
__heap_after_fork_parent();
|
||||||
call_fork_hooks(sParentHooks);
|
call_fork_hooks(sParentHooks);
|
||||||
mutex_unlock(&sForkLock);
|
mutex_unlock(&sForkLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
undefer_signals();
|
||||||
|
|
||||||
|
if (thread < 0) {
|
||||||
|
// something went wrong
|
||||||
|
__set_errno(thread);
|
||||||
|
thread = -1;
|
||||||
|
}
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user