| #include <unistd.h> |
| #include <errno.h> |
| #include "libc.h" |
| #include "lock.h" |
| #include "pthread_impl.h" |
| #include "fork_impl.h" |
| |
| static volatile int *const dummy_lockptr = 0; |
| |
| weak_alias(dummy_lockptr, __at_quick_exit_lockptr); |
| weak_alias(dummy_lockptr, __atexit_lockptr); |
| weak_alias(dummy_lockptr, __dlerror_lockptr); |
| weak_alias(dummy_lockptr, __gettext_lockptr); |
| weak_alias(dummy_lockptr, __locale_lockptr); |
| weak_alias(dummy_lockptr, __random_lockptr); |
| weak_alias(dummy_lockptr, __sem_open_lockptr); |
| weak_alias(dummy_lockptr, __stdio_ofl_lockptr); |
| weak_alias(dummy_lockptr, __syslog_lockptr); |
| weak_alias(dummy_lockptr, __timezone_lockptr); |
| weak_alias(dummy_lockptr, __bump_lockptr); |
| |
| weak_alias(dummy_lockptr, __vmlock_lockptr); |
| |
| static volatile int *const *const atfork_locks[] = { |
| &__at_quick_exit_lockptr, |
| &__atexit_lockptr, |
| &__dlerror_lockptr, |
| &__gettext_lockptr, |
| &__locale_lockptr, |
| &__random_lockptr, |
| &__sem_open_lockptr, |
| &__stdio_ofl_lockptr, |
| &__syslog_lockptr, |
| &__timezone_lockptr, |
| &__bump_lockptr, |
| }; |
| |
| static void dummy(int x) { } |
| weak_alias(dummy, __fork_handler); |
| weak_alias(dummy, __malloc_atfork); |
| weak_alias(dummy, __ldso_atfork); |
| |
| static void dummy_0(void) { } |
| weak_alias(dummy_0, __tl_lock); |
| weak_alias(dummy_0, __tl_unlock); |
| |
| pid_t fork(void) |
| { |
| sigset_t set; |
| __fork_handler(-1); |
| __block_app_sigs(&set); |
| int need_locks = libc.need_locks > 0; |
| if (need_locks) { |
| __ldso_atfork(-1); |
| __inhibit_ptc(); |
| for (int i=0; i<sizeof atfork_locks/sizeof *atfork_locks; i++) |
| if (*atfork_locks[i]) LOCK(*atfork_locks[i]); |
| __malloc_atfork(-1); |
| __tl_lock(); |
| } |
| pthread_t self=__pthread_self(), next=self->next; |
| pid_t ret = _Fork(); |
| int errno_save = errno; |
| if (need_locks) { |
| if (!ret) { |
| for (pthread_t td=next; td!=self; td=td->next) |
| td->tid = -1; |
| if (__vmlock_lockptr) { |
| __vmlock_lockptr[0] = 0; |
| __vmlock_lockptr[1] = 0; |
| } |
| } |
| __tl_unlock(); |
| __malloc_atfork(!ret); |
| for (int i=0; i<sizeof atfork_locks/sizeof *atfork_locks; i++) |
| if (*atfork_locks[i]) |
| if (ret) UNLOCK(*atfork_locks[i]); |
| else **atfork_locks[i] = 0; |
| __release_ptc(); |
| __ldso_atfork(!ret); |
| } |
| __restore_sigs(&set); |
| __fork_handler(!ret); |
| if (ret<0) errno = errno_save; |
| return ret; |
| } |