summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2021-09-01 23:32:14 +0300
committerIvan Maidanski <ivmai@mail.ru>2021-09-12 13:18:23 +0300
commit2abe67a5d58ab1aa36fac3600853146b0586a242 (patch)
tree6f7537e547bf669e2994e83a4187e3161b29327f
parentfce69e13848c41f7fd04ed9d17ad54fc49f2a234 (diff)
downloadbdwgc-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.h11
-rw-r--r--os_dep.c68
-rw-r--r--pthread_support.c5
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 */
diff --git a/os_dep.c b/os_dep.c
index f896d881..d19639dd 100644
--- a/os_dep.c
+++ b/os_dep.c
@@ -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 */