use crate::config::GlobalRng;
#[inline]
pub fn deterministic_start_index(num_branches: usize) -> usize {
if num_branches > 1 {
GlobalRng::random_range(0..num_branches)
} else {
0
}
}
pub enum Out2<A, B, E> {
_0(A),
_1(B),
_Else(E),
}
pub enum Out3<A, B, C, E> {
_0(A),
_1(B),
_2(C),
_Else(E),
}
pub enum Out4<A, B, C, D, E> {
_0(A),
_1(B),
_2(C),
_3(D),
_Else(E),
}
pub enum Out5<A, B, C, D, E, F> {
_0(A),
_1(B),
_2(C),
_3(D),
_4(E),
_Else(F),
}
pub enum Out6<A, B, C, D, E, F, G> {
_0(A),
_1(B),
_2(C),
_3(D),
_4(E),
_5(F),
_Else(G),
}
pub enum Out7<A, B, C, D, E, F, G, H> {
_0(A),
_1(B),
_2(C),
_3(D),
_4(E),
_5(F),
_6(G),
_Else(H),
}
#[macro_export]
macro_rules! deterministic_select {
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out2;
let start = $crate::util::deterministic_select::deterministic_start_index(2);
let order: [usize; 2] = [start % 2, (start + 1) % 2];
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
tokio::pin!(fut1);
tokio::pin!(fut2);
std::future::poll_fn(|cx| {
for &idx in &order {
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_1(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out2::_0($pat1) => $body1,
Out2::_1($pat2) => $body2,
Out2::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr, if $guard1:expr => $body1:expr,
$pat2:pat = $fut2:expr, if $guard2:expr => $body2:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out2;
let disabled: u8 = {
let mut d = 0u8;
if !$guard1 {
d |= 1 << 0;
}
if !$guard2 {
d |= 1 << 1;
}
d
};
let mut order: [usize; 2] = [0, 0];
let mut enabled_count = 0usize;
if disabled & (1 << 0) == 0 {
order[enabled_count] = 0;
enabled_count += 1;
}
if disabled & (1 << 1) == 0 {
order[enabled_count] = 1;
enabled_count += 1;
}
if enabled_count == 0 {
panic!("all branches are disabled and there is no else branch");
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
if start == 1 {
order.swap(0, 1);
}
}
let output = {
let mut fut1 = $fut1;
let mut fut2 = $fut2;
tokio::pin!(fut1);
tokio::pin!(fut2);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_1(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out2::_0($pat1) => $body1,
Out2::_1($pat2) => $body2,
Out2::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr, if $guard1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out2;
let disabled: u8 = if !$guard1 { 1 << 0 } else { 0 };
let mut order: [usize; 2] = [0, 0];
let mut enabled_count = 0usize;
if disabled & (1 << 0) == 0 {
order[enabled_count] = 0;
enabled_count += 1;
}
order[enabled_count] = 1;
enabled_count += 1;
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
if start == 1 {
order.swap(0, 1);
}
}
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
tokio::pin!(fut1);
tokio::pin!(fut2);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_1(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out2::_0($pat1) => $body1,
Out2::_1($pat2) => $body2,
Out2::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr, if $guard2:expr => $body2:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out2;
let disabled: u8 = if !$guard2 { 1 << 1 } else { 0 };
let mut order: [usize; 2] = [0, 0];
let mut enabled_count = 0usize;
order[enabled_count] = 0;
enabled_count += 1; if disabled & (1 << 1) == 0 {
order[enabled_count] = 1;
enabled_count += 1;
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
if start == 1 {
order.swap(0, 1);
}
}
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
tokio::pin!(fut1);
tokio::pin!(fut2);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out2::<_, _, ()>::_1(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out2::_0($pat1) => $body1,
Out2::_1($pat2) => $body2,
Out2::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr,
$pat3:pat = $fut3:expr => $body3:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out3;
let start = $crate::util::deterministic_select::deterministic_start_index(3);
let order: [usize; 3] = [start % 3, (start + 1) % 3, (start + 2) % 3];
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
let fut3 = $fut3;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
std::future::poll_fn(|cx| {
for &idx in &order {
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_2(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out3::_0($pat1) => $body1,
Out3::_1($pat2) => $body2,
Out3::_2($pat3) => $body3,
Out3::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr,
$pat3:pat = $fut3:expr, if $guard3:expr => $body3:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out3;
let disabled: u8 = if !$guard3 { 1 << 2 } else { 0 };
let mut order: [usize; 3] = [0, 1, 0];
let mut enabled_count = 2usize; if disabled & (1 << 2) == 0 {
order[enabled_count] = 2;
enabled_count += 1;
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
order[..enabled_count].rotate_left(start);
}
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
let fut3 = $fut3;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_2(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out3::_0($pat1) => $body1,
Out3::_1($pat2) => $body2,
Out3::_2($pat3) => $body3,
Out3::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr, if $guard2:expr => $body2:expr,
$pat3:pat = $fut3:expr, if $guard3:expr => $body3:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out3;
let disabled: u8 = {
let mut d = 0u8;
if !$guard2 {
d |= 1 << 1;
}
if !$guard3 {
d |= 1 << 2;
}
d
};
let mut order: [usize; 3] = [0, 0, 0];
let mut enabled_count = 1usize; if disabled & (1 << 1) == 0 {
order[enabled_count] = 1;
enabled_count += 1;
}
if disabled & (1 << 2) == 0 {
order[enabled_count] = 2;
enabled_count += 1;
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
order[..enabled_count].rotate_left(start);
}
let output = {
let mut fut1 = $fut1;
let mut fut2 = $fut2;
let mut fut3 = $fut3;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out3::<_, _, _, ()>::_2(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out3::_0($pat1) => $body1,
Out3::_1($pat2) => $body2,
Out3::_2($pat3) => $body3,
Out3::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr,
$pat3:pat = $fut3:expr => $body3:expr,
$pat4:pat = $fut4:expr => $body4:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out4;
let start = $crate::util::deterministic_select::deterministic_start_index(4);
let order: [usize; 4] = [start % 4, (start + 1) % 4, (start + 2) % 4, (start + 3) % 4];
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
let fut3 = $fut3;
let fut4 = $fut4;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
tokio::pin!(fut4);
std::future::poll_fn(|cx| {
for &idx in &order {
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_2(val));
}
}
3 => {
if let Poll::Ready(val) = fut4.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_3(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out4::_0($pat1) => $body1,
Out4::_1($pat2) => $body2,
Out4::_2($pat3) => $body3,
Out4::_3($pat4) => $body4,
Out4::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr,
$pat3:pat = $fut3:expr, if $guard3:expr => $body3:expr,
$pat4:pat = $fut4:expr => $body4:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out4;
let disabled: u8 = if !$guard3 { 1 << 2 } else { 0 };
let mut order: [usize; 4] = [0, 1, 3, 0];
let mut enabled_count = 3usize; if disabled & (1 << 2) == 0 {
order[3] = order[2];
order[2] = 2;
enabled_count = 4;
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
order[..enabled_count].rotate_left(start);
}
let output = {
let mut fut1 = $fut1;
let mut fut2 = $fut2;
let mut fut3 = $fut3;
let mut fut4 = $fut4;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
tokio::pin!(fut4);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_2(val));
}
}
3 => {
if let Poll::Ready(val) = fut4.as_mut().poll(cx) {
return Poll::Ready(Out4::<_, _, _, _, ()>::_3(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out4::_0($pat1) => $body1,
Out4::_1($pat2) => $body2,
Out4::_2($pat3) => $body3,
Out4::_3($pat4) => $body4,
Out4::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr => $body2:expr,
$pat3:pat = $fut3:expr => $body3:expr,
$pat4:pat = $fut4:expr => $body4:expr,
$pat5:pat = $fut5:expr => $body5:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out5;
let start = $crate::util::deterministic_select::deterministic_start_index(5);
let order: [usize; 5] = [
start % 5,
(start + 1) % 5,
(start + 2) % 5,
(start + 3) % 5,
(start + 4) % 5,
];
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
let fut3 = $fut3;
let fut4 = $fut4;
let fut5 = $fut5;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
tokio::pin!(fut4);
tokio::pin!(fut5);
std::future::poll_fn(|cx| {
for &idx in &order {
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_2(val));
}
}
3 => {
if let Poll::Ready(val) = fut4.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_3(val));
}
}
4 => {
if let Poll::Ready(val) = fut5.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_4(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out5::_0($pat1) => $body1,
Out5::_1($pat2) => $body2,
Out5::_2($pat3) => $body3,
Out5::_3($pat4) => $body4,
Out5::_4($pat5) => $body5,
Out5::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr, if $guard2:expr => $body2:expr,
$pat3:pat = $fut3:expr, if $guard3:expr => $body3:expr,
$pat4:pat = $fut4:expr => $body4:expr,
$pat5:pat = $fut5:expr, if $guard5:expr => $body5:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out5;
let disabled: u8 = {
let mut d = 0u8;
if !$guard2 {
d |= 1 << 1;
}
if !$guard3 {
d |= 1 << 2;
}
if !$guard5 {
d |= 1 << 4;
}
d
};
let mut order: [usize; 5] = [0, 3, 0, 0, 0];
let mut enabled_count = 2usize;
if disabled & (1 << 1) == 0 {
order[enabled_count] = 1;
enabled_count += 1;
}
if disabled & (1 << 2) == 0 {
order[enabled_count] = 2;
enabled_count += 1;
}
if disabled & (1 << 4) == 0 {
order[enabled_count] = 4;
enabled_count += 1;
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
order[..enabled_count].rotate_left(start);
}
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
let fut3 = $fut3;
let fut4 = $fut4;
let fut5 = $fut5;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
tokio::pin!(fut4);
tokio::pin!(fut5);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_2(val));
}
}
3 => {
if let Poll::Ready(val) = fut4.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_3(val));
}
}
4 => {
if let Poll::Ready(val) = fut5.as_mut().poll(cx) {
return Poll::Ready(Out5::<_, _, _, _, _, ()>::_4(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out5::_0($pat1) => $body1,
Out5::_1($pat2) => $body2,
Out5::_2($pat3) => $body3,
Out5::_3($pat4) => $body4,
Out5::_4($pat5) => $body5,
Out5::_Else(()) => unreachable!(),
}
}};
(
$pat1:pat = $fut1:expr => $body1:expr,
$pat2:pat = $fut2:expr, if $guard2:expr => $body2:expr,
$pat3:pat = $fut3:expr, if $guard3:expr => $body3:expr,
$pat4:pat = $fut4:expr => $body4:expr,
$pat5:pat = $fut5:expr => $body5:expr,
$pat6:pat = $fut6:expr => $body6:expr,
$pat7:pat = $fut7:expr => $body7:expr $(,)?
) => {{
use std::future::Future;
use std::task::Poll;
use $crate::util::deterministic_select::Out7;
let disabled: u8 = {
let mut d = 0u8;
if !$guard2 {
d |= 1 << 1;
}
if !$guard3 {
d |= 1 << 2;
}
d
};
let mut order: [usize; 7] = [0, 3, 4, 5, 6, 0, 0];
let mut enabled_count = 5usize;
if disabled & (1 << 1) == 0 {
order[enabled_count] = 1;
enabled_count += 1;
}
if disabled & (1 << 2) == 0 {
order[enabled_count] = 2;
enabled_count += 1;
}
if enabled_count > 1 {
let start =
$crate::util::deterministic_select::deterministic_start_index(enabled_count);
order[..enabled_count].rotate_left(start);
}
let output = {
let fut1 = $fut1;
let fut2 = $fut2;
let fut3 = $fut3;
let fut4 = $fut4;
let fut5 = $fut5;
let fut6 = $fut6;
let fut7 = $fut7;
tokio::pin!(fut1);
tokio::pin!(fut2);
tokio::pin!(fut3);
tokio::pin!(fut4);
tokio::pin!(fut5);
tokio::pin!(fut6);
tokio::pin!(fut7);
std::future::poll_fn(|cx| {
for i in 0..enabled_count {
let idx = order[i];
match idx {
0 => {
if let Poll::Ready(val) = fut1.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_0(val));
}
}
1 => {
if let Poll::Ready(val) = fut2.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_1(val));
}
}
2 => {
if let Poll::Ready(val) = fut3.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_2(val));
}
}
3 => {
if let Poll::Ready(val) = fut4.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_3(val));
}
}
4 => {
if let Poll::Ready(val) = fut5.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_4(val));
}
}
5 => {
if let Poll::Ready(val) = fut6.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_5(val));
}
}
6 => {
if let Poll::Ready(val) = fut7.as_mut().poll(cx) {
return Poll::Ready(Out7::<_, _, _, _, _, _, _, ()>::_6(val));
}
}
_ => unreachable!(),
}
}
Poll::Pending
})
.await
};
match output {
Out7::_0($pat1) => $body1,
Out7::_1($pat2) => $body2,
Out7::_2($pat3) => $body3,
Out7::_3($pat4) => $body4,
Out7::_4($pat5) => $body5,
Out7::_5($pat6) => $body6,
Out7::_6($pat7) => $body7,
Out7::_Else(()) => unreachable!(),
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test(flavor = "current_thread")]
async fn test_select2_determinism() {
GlobalRng::set_seed(12345);
let mut results_run1 = Vec::new();
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
};
results_run1.push(result);
}
GlobalRng::set_seed(12345);
let mut results_run2 = Vec::new();
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
};
results_run2.push(result);
}
assert_eq!(
results_run1, results_run2,
"Results should be deterministic with same seed"
);
let ones = results_run1.iter().filter(|&&x| x == 1).count();
let twos = results_run1.iter().filter(|&&x| x == 2).count();
assert!(
ones > 0 && twos > 0,
"Should select from both branches, got {} ones and {} twos",
ones,
twos
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select2_different_seeds_differ() {
GlobalRng::set_seed(11111);
let mut results_seed1 = Vec::new();
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
};
results_seed1.push(result);
}
GlobalRng::set_seed(22222);
let mut results_seed2 = Vec::new();
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
};
results_seed2.push(result);
}
assert_ne!(
results_seed1, results_seed2,
"Different seeds should produce different results"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select3_determinism() {
GlobalRng::set_seed(54321);
let mut results_run1 = Vec::new();
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
v = std::future::ready(3) => v,
};
results_run1.push(result);
}
GlobalRng::set_seed(54321);
let mut results_run2 = Vec::new();
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
v = std::future::ready(3) => v,
};
results_run2.push(result);
}
assert_eq!(
results_run1, results_run2,
"Results should be deterministic with same seed"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_with_single_ready_future() {
GlobalRng::set_seed(12345);
for _ in 0..10 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::pending::<i32>() => v,
};
assert_eq!(result, 1, "Ready future should always win against pending");
}
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_pattern_binding() {
GlobalRng::set_seed(12345);
let result = deterministic_select! {
val = std::future::ready(42) => val * 2,
val = std::future::ready(10) => val + 5,
};
assert!(
result == 84 || result == 15,
"Got unexpected result: {}",
result
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_with_borrowed_data() {
struct Data {
value: i32,
}
impl Data {
async fn get_a(&self) -> i32 {
self.value
}
async fn get_b(&self) -> i32 {
self.value * 2
}
}
let data = Data { value: 21 };
GlobalRng::set_seed(12345);
let result = deterministic_select! {
v = data.get_a() => v,
v = data.get_b() => v,
};
assert!(
result == 21 || result == 42,
"Got unexpected result: {}",
result
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_different_return_types() {
GlobalRng::set_seed(12345);
let result: i32 = deterministic_select! {
v = std::future::ready(42i32) => v,
_ = std::future::ready(()) => 0,
};
assert!(
result == 42 || result == 0,
"Got unexpected result: {}",
result
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_with_if_guard() {
GlobalRng::set_seed(12345);
let should_enable = false;
let result = deterministic_select! {
v = std::future::ready(1), if should_enable => v,
v = std::future::ready(2) => v,
};
assert_eq!(
result, 2,
"When guard is false, only branch 2 should be selected"
);
let should_enable = true;
let mut got_1 = false;
let mut got_2 = false;
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1), if should_enable => v,
v = std::future::ready(2) => v,
};
if result == 1 {
got_1 = true;
}
if result == 2 {
got_2 = true;
}
}
assert!(
got_1 && got_2,
"When guard is true, both branches should be selectable"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_with_if_guard_second_branch() {
GlobalRng::set_seed(12345);
let should_enable = false;
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2), if should_enable => v,
};
assert_eq!(
result, 1,
"When guard is false, only branch 1 should be selected"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_select3_with_guard_on_third() {
GlobalRng::set_seed(12345);
let should_enable = false;
let mut results = Vec::new();
for _ in 0..50 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
v = std::future::ready(3), if should_enable => v,
};
results.push(result);
}
assert!(
results.iter().all(|&r| r != 3),
"Branch 3 should never be selected when guard is false"
);
let should_enable = true;
let mut got_3 = false;
for _ in 0..100 {
let result = deterministic_select! {
v = std::future::ready(1) => v,
v = std::future::ready(2) => v,
v = std::future::ready(3), if should_enable => v,
};
if result == 3 {
got_3 = true;
}
}
assert!(got_3, "Branch 3 should be selectable when guard is true");
}
#[tokio::test(flavor = "current_thread")]
async fn test_select_guard_determinism() {
GlobalRng::set_seed(99999);
let mut results_run1 = Vec::new();
for i in 0..100 {
let guard = i % 2 == 0;
let result = deterministic_select! {
v = std::future::ready(1), if guard => v,
v = std::future::ready(2) => v,
};
results_run1.push(result);
}
GlobalRng::set_seed(99999);
let mut results_run2 = Vec::new();
for i in 0..100 {
let guard = i % 2 == 0;
let result = deterministic_select! {
v = std::future::ready(1), if guard => v,
v = std::future::ready(2) => v,
};
results_run2.push(result);
}
assert_eq!(
results_run1, results_run2,
"Results should be deterministic with same seed and guards"
);
}
}