yuyr 2a0b5067bd
Some checks failed
ci / public-tree (push) Has been cancelled
ci / docs (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / docker-runtime (push) Has been cancelled
20260902 完成公开树清理与热点拆分
2026-09-02 11:48:36 +08:00

256 lines
7.9 KiB
Rust

// Worker-pool channels, lifecycle, and worker loops.
enum RepoWorkerMessage {
Task(RepoSyncTask),
Shutdown,
}
enum TransportWorkerMessage {
Task(RepoTransportTask),
Shutdown,
}
pub struct RepoWorkerPool<E: RepoSyncExecutor> {
config: RepoWorkerPoolConfig,
task_tx: Sender<RepoWorkerMessage>,
result_rx: Receiver<RepoSyncResultEnvelope>,
workers: Vec<JoinHandle<()>>,
_executor: Arc<E>,
}
pub struct RepoTransportWorkerPool<E: RepoTransportExecutor> {
config: RepoWorkerPoolConfig,
task_tx: Sender<TransportWorkerMessage>,
result_rx: Receiver<RepoTransportResultEnvelope>,
workers: Vec<JoinHandle<()>>,
_executor: Arc<E>,
}
impl<E: RepoSyncExecutor> RepoWorkerPool<E> {
pub fn new(config: RepoWorkerPoolConfig, executor: E) -> Result<Self, String> {
if config.max_workers == 0 {
return Err("RepoWorkerPool requires at least one worker".to_string());
}
let executor = Arc::new(executor);
let (task_tx, task_rx) = mpsc::channel::<RepoWorkerMessage>();
let (result_tx, result_rx) = mpsc::channel::<RepoSyncResultEnvelope>();
let shared_task_rx = Arc::new(Mutex::new(task_rx));
let mut workers = Vec::with_capacity(config.max_workers);
for idx in 0..config.max_workers {
let task_rx = Arc::clone(&shared_task_rx);
let result_tx = result_tx.clone();
let executor = Arc::clone(&executor);
workers.push(
thread::Builder::new()
.name(format!("repo-sync-worker-{idx}"))
.spawn(move || worker_loop(task_rx, result_tx, executor))
.map_err(|e| format!("spawn repo worker failed: {e}"))?,
);
}
Ok(Self {
config,
task_tx,
result_rx,
workers,
_executor: executor,
})
}
pub fn worker_count(&self) -> usize {
self.config.max_workers
}
pub fn submit(&self, task: RepoSyncTask) -> Result<(), String> {
self.task_tx
.send(RepoWorkerMessage::Task(task))
.map_err(|e| format!("submit repo task failed: {e}"))
}
pub fn recv_result_timeout(
&self,
timeout: Duration,
) -> Result<Option<RepoSyncResultEnvelope>, String> {
match self.result_rx.recv_timeout(timeout) {
Ok(msg) => Ok(Some(msg)),
Err(RecvTimeoutError::Timeout) => Ok(None),
Err(RecvTimeoutError::Disconnected) => {
Err("repo worker result channel disconnected".to_string())
}
}
}
pub fn shutdown(mut self) -> Result<(), String> {
self.shutdown_inner()
}
fn shutdown_inner(&mut self) -> Result<(), String> {
if self.workers.is_empty() {
return Ok(());
}
for _ in 0..self.workers.len() {
self.task_tx
.send(RepoWorkerMessage::Shutdown)
.map_err(|e| format!("send shutdown to repo worker failed: {e}"))?;
}
let mut first_err: Option<String> = None;
for handle in self.workers.drain(..) {
if let Err(e) = handle.join() {
if first_err.is_none() {
first_err = Some(format!("join repo worker failed: {e:?}"));
}
}
}
if let Some(err) = first_err {
return Err(err);
}
Ok(())
}
}
impl<E: RepoSyncExecutor> Drop for RepoWorkerPool<E> {
fn drop(&mut self) {
let _ = self.shutdown_inner();
}
}
impl<E: RepoTransportExecutor> RepoTransportWorkerPool<E> {
pub fn new(config: RepoWorkerPoolConfig, executor: E) -> Result<Self, String> {
if config.max_workers == 0 {
return Err("RepoTransportWorkerPool requires at least one worker".to_string());
}
let executor = Arc::new(executor);
let (task_tx, task_rx) = mpsc::channel::<TransportWorkerMessage>();
let (result_tx, result_rx) = mpsc::channel::<RepoTransportResultEnvelope>();
let shared_task_rx = Arc::new(Mutex::new(task_rx));
let mut workers = Vec::with_capacity(config.max_workers);
for idx in 0..config.max_workers {
let task_rx = Arc::clone(&shared_task_rx);
let result_tx = result_tx.clone();
let executor = Arc::clone(&executor);
workers.push(
thread::Builder::new()
.name(format!("repo-transport-worker-{idx}"))
.spawn(move || transport_worker_loop(task_rx, result_tx, executor))
.map_err(|e| format!("spawn repo transport worker failed: {e}"))?,
);
}
Ok(Self {
config,
task_tx,
result_rx,
workers,
_executor: executor,
})
}
pub fn submit(&self, task: RepoTransportTask) -> Result<(), String> {
self.task_tx
.send(TransportWorkerMessage::Task(task))
.map_err(|e| format!("submit repo transport task failed: {e}"))
}
pub fn recv_result_timeout(
&self,
timeout: Duration,
) -> Result<Option<RepoTransportResultEnvelope>, String> {
match self.result_rx.recv_timeout(timeout) {
Ok(msg) => Ok(Some(msg)),
Err(RecvTimeoutError::Timeout) => Ok(None),
Err(RecvTimeoutError::Disconnected) => {
Err("repo transport worker result channel disconnected".to_string())
}
}
}
pub fn worker_count(&self) -> usize {
self.config.max_workers
}
pub fn shutdown(mut self) -> Result<(), String> {
self.shutdown_inner()
}
fn shutdown_inner(&mut self) -> Result<(), String> {
if self.workers.is_empty() {
return Ok(());
}
for _ in 0..self.workers.len() {
self.task_tx
.send(TransportWorkerMessage::Shutdown)
.map_err(|e| format!("send shutdown to repo transport worker failed: {e}"))?;
}
let mut first_err: Option<String> = None;
for handle in self.workers.drain(..) {
if let Err(e) = handle.join() {
if first_err.is_none() {
first_err = Some(format!("join repo transport worker failed: {e:?}"));
}
}
}
if let Some(err) = first_err {
return Err(err);
}
Ok(())
}
}
impl<E: RepoTransportExecutor> Drop for RepoTransportWorkerPool<E> {
fn drop(&mut self) {
let _ = self.shutdown_inner();
}
}
fn worker_loop<E: RepoSyncExecutor>(
task_rx: Arc<Mutex<Receiver<RepoWorkerMessage>>>,
result_tx: Sender<RepoSyncResultEnvelope>,
executor: Arc<E>,
) {
loop {
let message = {
let rx = task_rx.lock().expect("repo worker receiver lock poisoned");
rx.recv()
};
match message {
Ok(RepoWorkerMessage::Task(task)) => {
let result = executor.execute(task);
if result_tx.send(result).is_err() {
break;
}
}
Ok(RepoWorkerMessage::Shutdown) | Err(_) => break,
}
}
}
fn transport_worker_loop<E: RepoTransportExecutor>(
task_rx: Arc<Mutex<Receiver<TransportWorkerMessage>>>,
result_tx: Sender<RepoTransportResultEnvelope>,
executor: Arc<E>,
) {
loop {
let message = {
let rx = task_rx
.lock()
.expect("repo transport worker receiver lock poisoned");
rx.recv()
};
match message {
Ok(TransportWorkerMessage::Task(task)) => {
let result = executor.execute_transport(task);
if result_tx.send(result).is_err() {
break;
}
}
Ok(TransportWorkerMessage::Shutdown) | Err(_) => break,
}
}
}