|
1 | | -use anyhow::{Context, anyhow}; |
| 1 | +use anyhow::{anyhow, Context}; |
2 | 2 | use baa::{BitVecOps, BitVecValue}; |
3 | 3 | use log::info; |
4 | 4 | use protocols::{ |
@@ -868,6 +868,31 @@ impl Scheduler { |
868 | 868 | // This determines if we need to move threads to/from different queues |
869 | 869 | match thread.transaction[next_stmt_id] { |
870 | 870 | Stmt::Step => { |
| 871 | + // If the current statement itself is a `fork()`, |
| 872 | + // it means this thread started directly at the fork |
| 873 | + // (e.g. `exited_thread` from `handle_repeat_loops`, i.e. |
| 874 | + // the thread that exits the `repeat_loop` |
| 875 | + // with `loop_arg = Known(n)` set to some `n`). |
| 876 | + // We need to handle the fork (by returning `ExplicitFork` early) |
| 877 | + // before moving the current thread |
| 878 | + // to the `next` queue, but only if no other threads from the same |
| 879 | + // start cycle have already forked this cycle (to avoid duplicates) |
| 880 | + if matches!(thread.transaction[current_stmt_id], Stmt::Fork) { |
| 881 | + thread.has_forked = true; |
| 882 | + let already_forked = |
| 883 | + self.forked_start_cycles.contains(&thread.start_cycle); |
| 884 | + if !already_forked { |
| 885 | + self.forked_start_cycles.insert(thread.start_cycle); |
| 886 | + thread.current_stmt_id = next_stmt_id; |
| 887 | + return Ok(ThreadResult::ExplicitFork { |
| 888 | + parent: Box::new(thread), |
| 889 | + }); |
| 890 | + } |
| 891 | + // If another thread from the same start cycle, |
| 892 | + // has already forked in this cycle, |
| 893 | + // we just fall through to the normal Step logic below |
| 894 | + } |
| 895 | + |
871 | 896 | info!( |
872 | 897 | "Thread {} (transaction `{}`) called `step()`, moving to `next` queue", |
873 | 898 | thread.global_thread_id(ctx), |
|
0 commit comments