previously, if a file descriptor had aio operations pending in the
parent before fork, attempting to close it in the child would attempt
to cancel a thread belonging to the parent. this could deadlock, fail,
or crash the whole process of the cancellation signal handler was not
yet installed in the parent. in addition, further use of aio from the
child could malfunction or deadlock.
POSIX specifies that async io operations are not inherited by the
child on fork, so clear the entire aio fd map in the child, and take
the aio map lock (with signals blocked) across the fork so that the
lock is kept in a consistent state.
return fd;
}
+void __aio_atfork(int who)
+{
+ if (who<0) {
+ pthread_rwlock_rdlock(&maplock);
+ return;
+ }
+ if (who>0 && map) for (int a=0; a<(-1U/2+1)>>24; a++)
+ if (map[a]) for (int b=0; b<256; b++)
+ if (map[a][b]) for (int c=0; c<256; c++)
+ if (map[a][b][c]) for (int d=0; d<256; d++)
+ map[a][b][c][d] = 0;
+ pthread_rwlock_unlock(&maplock);
+}
+
weak_alias(aio_cancel, aio_cancel64);
weak_alias(aio_error, aio_error64);
weak_alias(aio_fsync, aio_fsync64);
extern hidden volatile int __aio_fut;
extern hidden volatile int __eintr_valid_flag;
+extern hidden void __aio_atfork(int);
+
hidden int __clone(int (*)(void *), void *, int, void *, ...);
hidden int __set_thread_area(void *);
hidden int __libc_sigaction(int, const struct sigaction *, struct sigaction *);
}
weak_alias(dummy, __fork_handler);
+weak_alias(dummy, __aio_atfork);
pid_t fork(void)
{
sigset_t set;
__fork_handler(-1);
__block_all_sigs(&set);
+ __aio_atfork(-1);
#ifdef SYS_fork
ret = __syscall(SYS_fork);
#else
libc.threads_minus_1 = 0;
if (libc.need_locks) libc.need_locks = -1;
}
+ __aio_atfork(!ret);
__restore_sigs(&set);
__fork_handler(!ret);
return __syscall_ret(ret);