import Std.Sync open Std def assertBEq [BEq α] [ToString α] (is should : α) : IO Unit := do if is != should then throw <| .userError s!"{is} should be {should}" def closeClose (ch : CloseableChannel Nat) : IO Unit := do assertBEq (← ch.isClosed) false assertBEq ((← EIO.toBaseIO ch.close) matches .ok ()) true assertBEq (← ch.isClosed) true assertBEq ((← EIO.toBaseIO ch.close) matches .error .alreadyClosed) true assertBEq (← ch.isClosed) true def paired (ch : CloseableChannel Nat) : IO Unit := do let sendTask ← ch.send 37 let recvTask ← ch.recv assertBEq ((← IO.wait sendTask) matches .ok ()) true assertBEq (← IO.wait recvTask) (some 37) def syncPaired (ch : CloseableChannel.Sync Nat) : IO Unit := do let sendTask ← IO.asTask (prio := .dedicated) (EIO.toBaseIO (ch.send 37)) let recvTask ← IO.asTask (prio := .dedicated) (ch.recv) assertBEq ((← IO.ofExcept (← IO.wait sendTask)) matches .ok ()) true assertBEq (← IO.ofExcept (← IO.wait recvTask)) (some 37) def trySend (ch : CloseableChannel Nat) (capacity : Option Nat) : IO Unit := do -- ready a receiver ahead of time let recvTask ← ch.recv assertBEq (← ch.trySend 37) true assertBEq (← IO.wait recvTask) (some 37) -- the unbounded CloseableChannel cannot go out of space so it is pointless to fill it up let some capacity := capacity | return () for i in *...capacity do assertBEq (← ch.trySend i) true assertBEq (← ch.trySend (capacity + 1)) false def tryRecv (ch : CloseableChannel Nat) : IO Unit := do assertBEq (← ch.tryRecv) none let sendTask ← ch.send 37 assertBEq (← ch.tryRecv) (some 37) assertBEq ((← IO.wait sendTask) matches .ok ()) true def sendRecvClose (ch : CloseableChannel Nat) : IO Unit := do let sendTask ← ch.send 37 assertBEq ((← EIO.toBaseIO ch.close) matches .ok ()) true let recvTask ← ch.recv assertBEq ((← IO.wait sendTask) matches .ok ()) true assertBEq (← IO.wait recvTask) (some 37) let sendTask ← ch.send 37 assertBEq ((← IO.wait sendTask) matches .error .closed) true let recvTask ← ch.recv assertBEq (← IO.wait recvTask) none assertBEq (← ch.trySend 37) false assertBEq (← ch.tryRecv) none def sendIt (ch : CloseableChannel Nat) (messages : List Nat) : BaseIO (Task (Option Unit)) := do match messages with | [] => return .pure <| some () | msg :: messages => BaseIO.bindTask (← ch.send msg) fun | .error .. => return .pure <| none | .ok .. => sendIt ch messages partial def recvIt (ch : CloseableChannel Nat) (messages : List Nat) : BaseIO (Task (List Nat)) := do BaseIO.bindTask (← ch.recv) fun | none => return .pure messages.reverse | some msg => recvIt ch (msg :: messages) def sendLots (ch : CloseableChannel Nat) : IO Unit := do let messages := List.range 1000 let sendTask ← sendIt ch messages let recvTask ← recvIt ch [] assertBEq (← IO.wait sendTask) (some ()) discard <| ch.close assertBEq (← IO.wait recvTask) messages def sendItSync (ch : CloseableChannel.Sync Nat) (messages : List Nat) : IO Unit := do for msg in messages do ch.send msg return () def recvItSync (ch : CloseableChannel.Sync Nat) : IO (List Nat) := do let mut messages := [] for msg in ch do messages := msg :: messages return messages.reverse def sendLotsSync (ch : CloseableChannel.Sync Nat) : IO Unit := do let messages := List.range 1000 let sendTask ← IO.asTask (prio := .dedicated) (sendItSync ch messages) let recvTask ← IO.asTask (prio := .dedicated) (recvItSync ch) IO.ofExcept (← IO.wait sendTask) discard <| ch.close assertBEq (← IO.ofExcept (← IO.wait recvTask)) messages partial def sendLotsMulti (ch : CloseableChannel Nat) : IO Unit := do let messages := List.range 1000 let sendTask1 ← sendIt ch messages let sendTask2 ← sendIt ch messages let recvTask1 ← recvIt ch [] let recvTask2 ← recvIt ch [] assertBEq (← IO.wait sendTask1) (some ()) assertBEq (← IO.wait sendTask2) (some ()) discard <| ch.close let msg1 ← IO.wait recvTask1 let msg2 ← IO.wait recvTask2 assertBEq (msg1.sum + msg2.sum) (2 * messages.sum) partial def sendLotsMultiSync (ch : CloseableChannel.Sync Nat) : IO Unit := do let messages := List.range 1000 let sendTask1 ← IO.asTask (prio := .dedicated) (sendItSync ch messages) let sendTask2 ← IO.asTask (prio := .dedicated) (sendItSync ch messages) let recvTask1 ← IO.asTask (prio := .dedicated) (recvItSync ch) let recvTask2 ← IO.asTask (prio := .dedicated) (recvItSync ch) IO.ofExcept (← IO.wait sendTask1) IO.ofExcept (← IO.wait sendTask2) discard <| ch.close let msg1 ← IO.ofExcept (← IO.wait recvTask1) let msg2 ← IO.ofExcept (← IO.wait recvTask2) assertBEq (msg1.sum + msg2.sum) (2 * messages.sum) def testIt (capacity : Option Nat) : IO Unit := do paired (← CloseableChannel.new capacity) syncPaired (← CloseableChannel.new capacity).sync closeClose (← CloseableChannel.new capacity) trySend (← CloseableChannel.new capacity) capacity tryRecv (← CloseableChannel.new capacity) sendRecvClose (← CloseableChannel.new capacity) sendLots (← CloseableChannel.new capacity) sendLotsSync (← CloseableChannel.new capacity).sync sendLotsMulti (← CloseableChannel.new capacity) sendLotsMultiSync (← CloseableChannel.new capacity).sync def suite : IO Unit := do testIt none testIt (some 0) testIt (some 1) testIt (some 8) testIt (some 128) #eval suite