diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2021-09-01 23:32:14 +0300 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2021-09-12 13:18:23 +0300 |
commit | 2abe67a5d58ab1aa36fac3600853146b0586a242 (patch) | |
tree | 6f7537e547bf669e2994e83a4187e3161b29327f | |
parent | fce69e13848c41f7fd04ed9d17ad54fc49f2a234 (diff) | |
download | bdwgc-2abe67a5d58ab1aa36fac3600853146b0586a242.tar.gz |
Fix GC_proc_fd value in child process at fork (Solaris)
(a cherry-pick of commit 6dca16f12 from 'release-7_4')
In case of PROC_VDB mode is initialized, GC_proc_fd contains a file
descriptor to "/proc/<pid>/pagedata". After forking, the child
process has a different pid value, thus the old file descriptor
should be closed and the one with the updated file name should be
opened in turn.
* include/private/gc_priv.h [!GC_DISABLE_INCREMENTAL
&& CAN_HANDLE_FORK] (GC_dirty_update_child): Declare (as a function
or as a macro).
* os_dep.c [PROC_VDB && !THREADS] (saved_proc_pid): New static
variable.
* os_dep.c [PROC_VDB] (GC_proc_fd): Initialize to -1 (instead of 0).
* os_dep.c [PROC_VDB] (proc_dirty_open_files): New static function
(part of code is moved from GC_dirty_init).
* os_dep.c [PROC_VDB && !THREADS] (proc_dirty_open_files): Store
pid to saved_proc_pid when true is returned.
* os_dep.c [PROC_VDB && CAN_HANDLE_FORK] (GC_dirty_update_child): New
GC_INNER function.
* os_dep.c [PROC_VDB] (GC_dirty_init): Call proc_dirty_open_files().
* os_dep.c [PROC_VDB && !THREADS] (GC_proc_read_dirty): Call
proc_dirty_open_files() if current pid differs from saved_proc_pid.
* pthread_support.c [CAN_HANDLE_FORK && !GC_DISABLE_INCREMENTAL]
(fork_child_proc): Call GC_dirty_update_child().
-rw-r--r-- | include/private/gc_priv.h | 11 | ||||
-rw-r--r-- | os_dep.c | 68 | ||||
-rw-r--r-- | pthread_support.c | 5 |
3 files changed, 70 insertions, 14 deletions
diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 144e2a65..7ed5a7b0 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -1982,6 +1982,17 @@ GC_EXTERN GC_bool GC_print_back_height; /* that it's not write protected by the virtual */ /* dirty bit implementation. */ +# ifdef CAN_HANDLE_FORK +# if defined(PROC_VDB) + GC_INNER void GC_dirty_update_child(void); + /* Update pid-specific resources (like /proc file */ + /* descriptors) needed by the dirty bits implementation */ + /* after fork in the child process. */ +# else +# define GC_dirty_update_child() (void)0 +# endif +# endif /* CAN_HANDLE_FORK */ + GC_INNER GC_bool GC_dirty_init(void); /* Returns true if dirty bits are maintained (otherwise */ /* it is OK to be called again if the client invokes */ @@ -3643,25 +3643,22 @@ ssize_t read(int fd, void *buf, size_t nbyte) # include <sys/procfs.h> # include <sys/stat.h> +# ifndef THREADS + static pid_t saved_proc_pid; /* pid used to compose /proc file name */ +# endif + # define INITIAL_BUF_SZ 16384 STATIC word GC_proc_buf_size = INITIAL_BUF_SZ; STATIC char *GC_proc_buf = NULL; - STATIC int GC_proc_fd = 0; + STATIC int GC_proc_fd = -1; -GC_INNER GC_bool GC_dirty_init(void) -{ + static GC_bool proc_dirty_open_files(void) + { int fd; char buf[30]; + pid_t pid = getpid(); - if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { - memset(GC_written_pages, 0xff, sizeof(page_hash_table)); - if (GC_print_stats == VERBOSE) - GC_log_printf("Allocated bytes:%lu:all pages may have been written\n", - (unsigned long)(GC_bytes_allocd - + GC_bytes_allocd_before_gc)); - } - - sprintf(buf, "/proc/%ld", (long)getpid()); + sprintf(buf, "/proc/%ld", (long)pid); fd = open(buf, O_RDONLY); if (fd < 0) { WARN("/proc open failed; cannot enable GC incremental mode\n", 0); @@ -3670,11 +3667,39 @@ GC_INNER GC_bool GC_dirty_init(void) GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0); close(fd); syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC); - if (GC_proc_fd < 0) { + if (-1 == GC_proc_fd) { WARN("/proc ioctl(PIOCOPENPD) failed", 0); return FALSE; } +# ifndef THREADS + saved_proc_pid = pid; /* updated on success only */ +# endif + return TRUE; + } +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + if (-1 == GC_proc_fd) + return; /* GC incremental mode is off */ + + close(GC_proc_fd); + if (!proc_dirty_open_files()) + GC_incremental = FALSE; /* should be safe to turn it off */ + } +# endif /* CAN_HANDLE_FORK */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + if (GC_print_stats == VERBOSE) + GC_log_printf("Allocated bytes:%lu: all pages may have been written\n", + (unsigned long)(GC_bytes_allocd + + GC_bytes_allocd_before_gc)); + } + if (!proc_dirty_open_files()) + return FALSE; GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); if (GC_proc_buf == NULL) ABORT("Insufficient space for /proc read"); @@ -3693,6 +3718,23 @@ GC_INNER void GC_read_dirty(void) char * bufp; int i; +# ifndef THREADS + /* If the current pid differs from the saved one, then we are in */ + /* the forked (child) process, the current /proc file should be */ + /* closed, the new one should be opened with the updated path. */ + /* Note, this is not needed for multi-threaded case because */ + /* fork_child_proc() reopens the file right after fork. */ + if (getpid() != saved_proc_pid + && (-1 == GC_proc_fd /* no need to retry */ + || (close(GC_proc_fd), !proc_dirty_open_files()))) { + /* Failed to reopen the file. Punt! */ + if (!output_unneeded) + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; + } +# endif + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); bufp = GC_proc_buf; if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { diff --git a/pthread_support.c b/pthread_support.c index 4d16191a..96e97cba 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -898,7 +898,10 @@ STATIC void GC_fork_child_proc(void) /* just going to exec, and we would have to restart mark threads. */ GC_markers = 1; GC_parallel = FALSE; -# endif /* PARALLEL_MARK */ +# endif +# ifndef GC_DISABLE_INCREMENTAL + GC_dirty_update_child(); +# endif RESTORE_CANCEL(fork_cancel_state); UNLOCK(); /* Even though after a fork the child only inherits the single */ |