Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(profiling) switch to pthread_atfork() for fork barrier handling #3058

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions profiling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pub mod capi;
mod clocks;
mod config;
mod logging;
mod pcntl;
pub mod profiling;
mod pthread;
mod sapi;
mod thin_str;
mod wall_time;
Expand Down Expand Up @@ -875,11 +875,14 @@ extern "C" fn startup(extension: *mut ZendExtension) -> ZendResult {

// Safety: calling this in zend_extension startup.
unsafe {
pcntl::startup();
pthread::startup();
timeline::timeline_startup();
}

#[cfg(all(feature = "allocation_profiling", not(php_zend_mm_set_custom_handlers_ex)))]
#[cfg(all(
feature = "allocation_profiling",
not(php_zend_mm_set_custom_handlers_ex)
))]
allocation::alloc_prof_startup();

ZendResult::Success
Expand Down
165 changes: 0 additions & 165 deletions profiling/src/pcntl.rs

This file was deleted.

48 changes: 48 additions & 0 deletions profiling/src/pthread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::allocation::alloc_prof_rshutdown;
use crate::{config, Profiler};
use log::trace;

/// # Safety
/// Only call this from zend_extension's startup function. It's not designed
/// to be called from anywhere else.
pub(crate) unsafe fn startup() {
libc::pthread_atfork(Some(prepare), Some(parent), Some(child));
}

extern "C" fn prepare() {
// Hold mutexes across the handler. If there are any spurious wakeups by
// the threads while the fork is occurring, they cannot acquire locks
// since this thread holds them, preventing a deadlock situation.
if let Some(profiler) = Profiler::get() {
trace!("Preparing profiler for upcomming fork call.");
let _ = profiler.fork_prepare();
}
}

extern "C" fn parent() {
if let Some(profiler) = Profiler::get() {
trace!("Re-enabling profiler in parent after fork call.");
profiler.post_fork_parent();
}
}

unsafe extern "C" fn child() {
if Profiler::get().is_none() {
// No profiler, so nothing to do. This can happen in Apache forking SAPI, where Apache
// would first go through MINIT phase and then fork(), so we'd observe the fork but there
// is no profiler yet.
return;
}
trace!("Shutting down profiler for child process after fork");
// Disable the profiler because this is the child, and we don't support this yet.
// And then leak the old profiler. Its drop method is not safe to run in these situations.
Profiler::kill();

alloc_prof_rshutdown();

// Reset some global state to prevent further profiling and to not handle
// any pending interrupts.
// SAFETY: done after config is used to shut down other things, and in a
// thread-safe context.
config::on_fork_in_child();
}
Loading