mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
event_monitor: refactor the implementation to support concurrent access
This patch modifies `event_monitor` to ensure that concurrent access to `event_log` from multiple threads is safe. Previously, the `event_log` function would acquire a reference to the event log file and write to it without doing any synchronization, which made it prone to data races. This issue likely went under the radar because the relevant `SAFETY` comment on the unsafe block was incomplete. The new implementation spawns a dedicated thread named `event-monitor` solely for writing to the file. It uses the MPMC channel exposed by `flume` to pass messages to the `event-monitor` thread. Since `flume::Sender<T>` implements `Sync`, it is safe for multiple threads to share it and send messages to the `event-monitor` thread. This is not possible with `std::sync::mpsc::Sender<T>` since it's `!Sync`, meaning it is not safe for it to be shared between different threads. The `event_monitor::set_monitor` function now only initializes the required global state and returns an instance of the `Monitor` struct. This decouples the actual logging logic from the `event_monitor` crate. The `event-monitor` thread is then spawned by the `vmm` crate. Signed-off-by: Omer Faruk Bayram <omer.faruk@sartura.hr>
This commit is contained in:
committed by
Rob Bradford
parent
3d82867fe2
commit
02e1c54426
61
src/main.rs
61
src/main.rs
@@ -69,6 +69,8 @@ enum Error {
|
||||
BareEventMonitor,
|
||||
#[error("Error doing event monitor I/O: {0}")]
|
||||
EventMonitorIo(std::io::Error),
|
||||
#[error("Event monitor thread failed: {0}")]
|
||||
EventMonitorThread(#[source] vmm::Error),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
#[error("Error parsing --gdb: {0}")]
|
||||
ParsingGdb(option_parser::OptionParserError),
|
||||
@@ -459,32 +461,6 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
|
||||
(None, None) => Ok(None),
|
||||
}?;
|
||||
|
||||
if let Some(ref monitor_config) = toplevel.event_monitor {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser
|
||||
.parse(monitor_config)
|
||||
.map_err(Error::ParsingEventMonitor)?;
|
||||
|
||||
let file = if parser.is_set("fd") {
|
||||
let fd = parser
|
||||
.convert("fd")
|
||||
.map_err(Error::ParsingEventMonitor)?
|
||||
.unwrap();
|
||||
// SAFETY: fd is valid
|
||||
unsafe { File::from_raw_fd(fd) }
|
||||
} else if parser.is_set("path") {
|
||||
std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(parser.get("path").unwrap())
|
||||
.map_err(Error::EventMonitorIo)?
|
||||
} else {
|
||||
return Err(Error::BareEventMonitor);
|
||||
};
|
||||
event_monitor::set_monitor(file).map_err(Error::EventMonitorIo)?;
|
||||
}
|
||||
|
||||
let (api_request_sender, api_request_receiver) = channel();
|
||||
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
|
||||
|
||||
@@ -531,8 +507,6 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
|
||||
}
|
||||
}
|
||||
|
||||
event!("vmm", "starting");
|
||||
|
||||
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
||||
|
||||
#[cfg(feature = "guest_debug")]
|
||||
@@ -556,6 +530,37 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
|
||||
|
||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
|
||||
|
||||
if let Some(ref monitor_config) = toplevel.event_monitor {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("fd");
|
||||
parser
|
||||
.parse(monitor_config)
|
||||
.map_err(Error::ParsingEventMonitor)?;
|
||||
|
||||
let file = if parser.is_set("fd") {
|
||||
let fd = parser
|
||||
.convert("fd")
|
||||
.map_err(Error::ParsingEventMonitor)?
|
||||
.unwrap();
|
||||
// SAFETY: fd is valid
|
||||
unsafe { File::from_raw_fd(fd) }
|
||||
} else if parser.is_set("path") {
|
||||
std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(parser.get("path").unwrap())
|
||||
.map_err(Error::EventMonitorIo)?
|
||||
} else {
|
||||
return Err(Error::BareEventMonitor);
|
||||
};
|
||||
|
||||
let monitor = event_monitor::set_monitor(file).map_err(Error::EventMonitorIo)?;
|
||||
vmm::start_event_monitor_thread(monitor, exit_evt.try_clone().unwrap())
|
||||
.map_err(Error::EventMonitorThread)?;
|
||||
}
|
||||
|
||||
event!("vmm", "starting");
|
||||
|
||||
let vmm_thread_handle = vmm::start_vmm_thread(
|
||||
vmm::VmmVersionInfo::new(env!("BUILD_VERSION"), env!("CARGO_PKG_VERSION")),
|
||||
&api_socket_path,
|
||||
|
||||
Reference in New Issue
Block a user