Skip to content

Commit ca4d86b

Browse files
committed
macos-linux thread parity
1 parent 0c69677 commit ca4d86b

File tree

1 file changed

+20
-92
lines changed

1 file changed

+20
-92
lines changed

crates/pecos-qis-core/src/ccengine.rs

Lines changed: 20 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,9 @@ use pecos_engines::{
2525
use pecos_qis_ffi_types::{Operation, OperationCollector as OperationList, QuantumOp};
2626
use pecos_rng::PecosRng;
2727
use std::collections::BTreeMap;
28-
use std::thread::JoinHandle;
29-
30-
// These imports are only used by PersistentDynamicWorker (Linux only)
31-
#[cfg(not(target_os = "macos"))]
3228
use std::sync::Mutex;
33-
#[cfg(not(target_os = "macos"))]
3429
use std::sync::mpsc::{self, Receiver, Sender};
30+
use std::thread::JoinHandle;
3531

3632
/// Result from worker thread - returns both the operations and the interface
3733
type WorkerResult = Result<(OperationList, BoxedInterface), String>;
@@ -49,15 +45,11 @@ struct DynamicExecutionState {
4945
execution_complete: bool,
5046
/// Sync handle for main thread FFI calls
5147
/// Uses the same library instance (singleton) as the worker thread,
52-
/// ensuring TLS consistency on macOS
48+
/// ensuring TLS consistency across platforms
5349
sync_handle: Option<Box<dyn DynamicSyncHandle>>,
54-
/// Worker thread handle (used on macOS where persistent worker causes issues)
55-
#[cfg(target_os = "macos")]
56-
worker_handle: Option<JoinHandle<WorkerResult>>,
5750
}
5851

5952
/// Work item sent to the persistent dynamic worker thread
60-
#[cfg(not(target_os = "macos"))]
6153
struct DynamicWorkItem {
6254
/// The interface to execute
6355
interface: BoxedInterface,
@@ -69,7 +61,6 @@ struct DynamicWorkItem {
6961
/// and TLS allocation issues that come from spawning a new thread per shot.
7062
/// The thread waits for work items via a channel, executes `collect_operations()`,
7163
/// and sends results back via another channel.
72-
#[cfg(not(target_os = "macos"))]
7364
struct PersistentDynamicWorker {
7465
/// Channel to send work items to the worker
7566
work_tx: Sender<DynamicWorkItem>,
@@ -79,7 +70,6 @@ struct PersistentDynamicWorker {
7970
_handle: JoinHandle<()>,
8071
}
8172

82-
#[cfg(not(target_os = "macos"))]
8373
impl PersistentDynamicWorker {
8474
/// Create a new persistent dynamic worker thread
8575
fn new() -> Self {
@@ -202,7 +192,6 @@ pub struct QisEngine {
202192

203193
/// Persistent worker thread for dynamic execution (stays alive across shots)
204194
/// This avoids spawning a new thread per shot, which causes TLS allocation issues.
205-
#[cfg(not(target_os = "macos"))]
206195
persistent_worker: Option<PersistentDynamicWorker>,
207196
}
208197

@@ -230,7 +219,6 @@ impl QisEngine {
230219
program_bytes: None,
231220
program_format: None,
232221
interface_builder: None,
233-
#[cfg(not(target_os = "macos"))]
234222
persistent_worker: None,
235223
}
236224
}
@@ -304,7 +292,6 @@ impl QisEngine {
304292
program_bytes: None,
305293
program_format: None,
306294
interface_builder: None,
307-
#[cfg(not(target_os = "macos"))]
308295
persistent_worker: None,
309296
}
310297
}
@@ -474,7 +461,6 @@ impl Clone for QisEngine {
474461
.as_ref()
475462
.map(|b| dyn_clone::clone_box(&**b)),
476463
// Create a new persistent worker for this clone (can't share threads across clones)
477-
#[cfg(not(target_os = "macos"))]
478464
persistent_worker: None,
479465
}
480466
}
@@ -484,8 +470,8 @@ impl Clone for QisEngine {
484470
impl QisEngine {
485471
/// Start the LLVM program execution in a worker thread
486472
///
487-
/// Uses a persistent worker thread on Linux to avoid glibc TLS allocation issues.
488-
/// On macOS, uses per-shot spawning with a sync handle that uses the singleton library.
473+
/// Uses a persistent worker thread to avoid TLS allocation issues from
474+
/// spawning a new thread per shot.
489475
fn start_dynamic_worker(&mut self) -> Result<(), PecosError> {
490476
debug!("Starting dynamic execution");
491477

@@ -516,55 +502,23 @@ impl QisEngine {
516502
PecosError::Generic("No interface available for dynamic execution".to_string())
517503
})?;
518504

519-
// Platform-specific worker spawning:
520-
// - On Linux: Use persistent worker to avoid glibc TLS allocation issues
521-
// - On macOS: Use per-shot spawning (persistent worker causes segfaults)
522-
#[cfg(not(target_os = "macos"))]
523-
{
524-
// Create persistent worker if it doesn't exist
525-
if self.persistent_worker.is_none() {
526-
debug!("Creating new persistent dynamic worker thread");
527-
self.persistent_worker = Some(PersistentDynamicWorker::new());
528-
}
529-
530-
// Send work to persistent worker
531-
self.persistent_worker
532-
.as_ref()
533-
.expect("persistent worker was just created")
534-
.execute(interface)?;
535-
536-
// Initialize dynamic state
537-
self.dynamic_state = Some(DynamicExecutionState {
538-
sync_handle,
539-
execution_complete: false,
540-
});
505+
// Create persistent worker if it doesn't exist
506+
if self.persistent_worker.is_none() {
507+
debug!("Creating new persistent dynamic worker thread");
508+
self.persistent_worker = Some(PersistentDynamicWorker::new());
541509
}
542510

543-
#[cfg(target_os = "macos")]
544-
{
545-
// Spawn worker thread that runs the LLVM program (per-shot)
546-
let worker_handle = std::thread::spawn(move || {
547-
let mut interface = interface; // Shadow with mutable binding
548-
debug!("Worker thread: starting collect_operations");
549-
let result = interface.collect_operations();
550-
debug!("Worker thread: collect_operations returned");
551-
552-
// Disable dynamic mode before returning
553-
let _ = interface.disable_dynamic_mode();
554-
555-
// Return both the result and the interface
556-
result
557-
.map(|collector| (collector, interface))
558-
.map_err(|e| e.to_string())
559-
});
560-
561-
// Initialize dynamic state with worker handle
562-
self.dynamic_state = Some(DynamicExecutionState {
563-
sync_handle,
564-
execution_complete: false,
565-
worker_handle: Some(worker_handle),
566-
});
567-
}
511+
// Send work to persistent worker
512+
self.persistent_worker
513+
.as_ref()
514+
.expect("persistent worker was just created")
515+
.execute(interface)?;
516+
517+
// Initialize dynamic state
518+
self.dynamic_state = Some(DynamicExecutionState {
519+
sync_handle,
520+
execution_complete: false,
521+
});
568522

569523
Ok(())
570524
}
@@ -633,38 +587,12 @@ impl QisEngine {
633587
return true;
634588
}
635589

636-
// Platform-specific result checking - get result outside of borrow
637-
#[cfg(not(target_os = "macos"))]
590+
// Check if persistent worker has a result ready
638591
let result: Option<WorkerResult> = self
639592
.persistent_worker
640593
.as_ref()
641594
.and_then(PersistentDynamicWorker::try_recv_result);
642595

643-
#[cfg(target_os = "macos")]
644-
let result: Option<WorkerResult> = {
645-
if let Some(ref mut state) = self.dynamic_state {
646-
if let Some(handle) = state.worker_handle.take() {
647-
if handle.is_finished() {
648-
if let Ok(r) = handle.join() {
649-
Some(r)
650-
} else {
651-
log::error!("Worker panicked");
652-
state.execution_complete = true;
653-
return true;
654-
}
655-
} else {
656-
// Put handle back if not finished
657-
state.worker_handle = Some(handle);
658-
None
659-
}
660-
} else {
661-
None
662-
}
663-
} else {
664-
None
665-
}
666-
};
667-
668596
// Process result if we got one
669597
if let Some(result) = result {
670598
match result {

0 commit comments

Comments
 (0)