LibC: Implement pthread_atfork
Again this code is not tested but *feels* right :D
This commit is contained in:
parent
dbdefa0f4a
commit
df7f245cf8
|
@ -82,6 +82,11 @@ struct uthread
|
||||||
#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t){ { PTHREAD_MUTEX_DEFAULT, 0 }, 0, 0 }
|
#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t){ { PTHREAD_MUTEX_DEFAULT, 0 }, 0, 0 }
|
||||||
#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t){ { 0 }, 0, 0 }
|
#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t){ { 0 }, 0, 0 }
|
||||||
|
|
||||||
|
#define _PTHREAD_ATFORK_PREPARE 0
|
||||||
|
#define _PTHREAD_ATFORK_PARENT 1
|
||||||
|
#define _PTHREAD_ATFORK_CHILD 2
|
||||||
|
void _pthread_call_atfork(int state);
|
||||||
|
|
||||||
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void));
|
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void));
|
||||||
int pthread_attr_destroy(pthread_attr_t* attr);
|
int pthread_attr_destroy(pthread_attr_t* attr);
|
||||||
int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate);
|
int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate);
|
||||||
|
|
|
@ -470,6 +470,102 @@ int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct pthread_atfork_t
|
||||||
|
{
|
||||||
|
void (*function)();
|
||||||
|
pthread_atfork_t* next;
|
||||||
|
};
|
||||||
|
static pthread_atfork_t* s_atfork_prepare = nullptr;
|
||||||
|
static pthread_atfork_t* s_atfork_parent = nullptr;
|
||||||
|
static pthread_atfork_t* s_atfork_child = nullptr;
|
||||||
|
static pthread_mutex_t s_atfork_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
void _pthread_call_atfork(int state)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&s_atfork_mutex);
|
||||||
|
|
||||||
|
pthread_atfork_t* list = nullptr;
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case _PTHREAD_ATFORK_PREPARE: list = s_atfork_prepare; break;
|
||||||
|
case _PTHREAD_ATFORK_PARENT: list = s_atfork_parent; break;
|
||||||
|
case _PTHREAD_ATFORK_CHILD: list = s_atfork_child; break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; list; list = list->next)
|
||||||
|
list->function();
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&s_atfork_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void))
|
||||||
|
{
|
||||||
|
pthread_atfork_t* prepare_entry = nullptr;
|
||||||
|
if (prepare != nullptr)
|
||||||
|
prepare_entry = static_cast<pthread_atfork_t*>(malloc(sizeof(pthread_attr_t)));
|
||||||
|
|
||||||
|
pthread_atfork_t* parent_entry = nullptr;
|
||||||
|
if (parent != nullptr)
|
||||||
|
parent_entry = static_cast<pthread_atfork_t*>(malloc(sizeof(pthread_attr_t)));
|
||||||
|
|
||||||
|
pthread_atfork_t* child_entry = nullptr;
|
||||||
|
if (child != nullptr)
|
||||||
|
child_entry = static_cast<pthread_atfork_t*>(malloc(sizeof(pthread_attr_t)));
|
||||||
|
|
||||||
|
if ((prepare && !prepare_entry) || (parent && !parent_entry) || (child && !child_entry))
|
||||||
|
{
|
||||||
|
if (prepare_entry)
|
||||||
|
free(prepare_entry);
|
||||||
|
if (parent_entry)
|
||||||
|
free(parent_entry);
|
||||||
|
if (child_entry)
|
||||||
|
free(child_entry);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto prepend_atfork =
|
||||||
|
[](pthread_atfork_t*& list, pthread_atfork_t* entry)
|
||||||
|
{
|
||||||
|
entry->next = list;
|
||||||
|
list = entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto append_atfork =
|
||||||
|
[](pthread_atfork_t*& list, pthread_atfork_t* entry)
|
||||||
|
{
|
||||||
|
while (list)
|
||||||
|
list = list->next;
|
||||||
|
entry->next = nullptr;
|
||||||
|
list = entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_atfork_mutex);
|
||||||
|
|
||||||
|
if (prepare_entry)
|
||||||
|
{
|
||||||
|
prepare_entry->function = prepare;
|
||||||
|
prepend_atfork(s_atfork_prepare, prepare_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent_entry)
|
||||||
|
{
|
||||||
|
parent_entry->function = parent;
|
||||||
|
append_atfork(s_atfork_parent, parent_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child_entry)
|
||||||
|
{
|
||||||
|
child_entry->function = parent;
|
||||||
|
append_atfork(s_atfork_child, child_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&s_atfork_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void pthread_cancel_handler(int)
|
static void pthread_cancel_handler(int)
|
||||||
{
|
{
|
||||||
uthread* uthread = _get_uthread();
|
uthread* uthread = _get_uthread();
|
||||||
|
|
|
@ -423,7 +423,12 @@ int execvp(const char* pathname, char* const argv[])
|
||||||
|
|
||||||
pid_t fork(void)
|
pid_t fork(void)
|
||||||
{
|
{
|
||||||
return syscall(SYS_FORK);
|
_pthread_call_atfork(_PTHREAD_ATFORK_PREPARE);
|
||||||
|
const pid_t pid = syscall(SYS_FORK);
|
||||||
|
if (pid == -1)
|
||||||
|
return -1;
|
||||||
|
_pthread_call_atfork(pid ? _PTHREAD_ATFORK_PARENT : _PTHREAD_ATFORK_CHILD);
|
||||||
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pipe(int fildes[2])
|
int pipe(int fildes[2])
|
||||||
|
|
Loading…
Reference in New Issue