(.using [library [lux "*" ["_" test {"+" Test}] ["@" target] [abstract ["[0]" monad {"+" do}] ["[0]" enum]] [control ["[0]" io] ["[0]" maybe] ["[0]" try] ["[0]" exception {"+" exception:}] [concurrency ["[0]" async {"+" Async}] ["[0]" atom {"+" Atom}]]] [data ["[0]" text ("[1]#[0]" equivalence) ["%" format {"+" format}]] [collection ["[0]" list ("[1]#[0]" functor)]]] [math ["[0]" random] [number ["n" nat] ["[0]" i64]]] [type ["[0]" refinement]]]] [\\library ["[0]" /]]) (def: delay (for @.js (i64.left_shifted 4 1) (i64.left_shifted 3 1))) (def: semaphore Test (_.for [/.Semaphore] (all _.and (do [! random.monad] [initial_open_positions (|> random.nat (# ! each (|>> (n.% 10) (n.max 1)))) .let [semaphore (/.semaphore initial_open_positions)]] (in (do async.monad [result (async.within ..delay (/.wait! semaphore))] (_.cover' [/.semaphore] (case result {.#Some _} true {.#None} false))))) (do [! random.monad] [initial_open_positions (|> random.nat (# ! each (|>> (n.% 10) (n.max 1)))) .let [semaphore (/.semaphore initial_open_positions)]] (in (do [! async.monad] [_ (monad.each ! /.wait! (list.repeated initial_open_positions semaphore)) result (async.within ..delay (/.wait! semaphore))] (_.cover' [/.wait!] (case result {.#Some _} false {.#None} true))))) (do [! random.monad] [initial_open_positions (|> random.nat (# ! each (|>> (n.% 10) (n.max 1)))) .let [semaphore (/.semaphore initial_open_positions)]] (in (do [! async.monad] [_ (monad.each ! /.wait! (list.repeated initial_open_positions semaphore)) .let [block (/.wait! semaphore)] result/0 (async.within ..delay block) open_positions (/.signal! semaphore) result/1 (async.within ..delay block)] (_.cover' [/.signal!] (case [result/0 result/1 open_positions] [{.#None} {.#Some _} {try.#Success +0}] true _ false))))) (do [! random.monad] [initial_open_positions (|> random.nat (# ! each (|>> (n.% 10) (n.max 1)))) .let [semaphore (/.semaphore initial_open_positions)]] (in (do async.monad [outcome (/.signal! semaphore)] (_.cover' [/.semaphore_is_maxed_out] (case outcome {try.#Failure error} (exception.match? /.semaphore_is_maxed_out error) _ false))))) ))) (def: mutex Test (_.for [/.Mutex] (all _.and (do [! random.monad] [repetitions (|> random.nat (# ! each (|>> (n.% 100) (n.max 10)))) .let [resource (atom.atom "") expected_As (text.together (list.repeated repetitions "A")) expected_Bs (text.together (list.repeated repetitions "B")) mutex (/.mutex []) processA (<| (/.synchronize! mutex) io.io async.future (do [! io.monad] [_ (<| (monad.all !) (list.repeated repetitions) (atom.update! (|>> (format "A")) resource))] (in []))) processB (<| (/.synchronize! mutex) io.io async.future (do [! io.monad] [_ (<| (monad.all !) (list.repeated repetitions) (atom.update! (|>> (format "B")) resource))] (in [])))]] (in (do async.monad [_ processA _ processB .let [outcome (io.run! (atom.read! resource))]] (_.cover' [/.mutex /.synchronize!] (or (text#= (format expected_As expected_Bs) outcome) (text#= (format expected_Bs expected_As) outcome)))))) ))) (def: (waiter resource barrier id) (-> (Atom Text) /.Barrier Nat (Async Any)) (do async.monad [_ (/.block! barrier) _ (async.future (atom.update! (|>> (format (%.nat id))) resource))] (in []))) (def: barrier Test (_.for [/.Barrier] (all _.and (do random.monad [raw random.nat] (_.cover [/.Limit /.limit] (case [raw (/.limit raw)] [0 {.#None}] true [_ {.#Some limit}] (and (n.> 0 raw) (n.= raw (refinement.value limit))) _ false))) (do [! random.monad] [limit (# ! each (|>> (n.% 9) ++) random.nat) .let [barrier (/.barrier (maybe.trusted (/.limit limit))) resource (atom.atom "")]] (in (do [! async.monad] [.let [suffix "_" expected_ending (|> suffix (list.repeated limit) text.together) expected_ids (enum.range n.enum 0 (-- limit))] _ (|> expected_ids (list#each (function (_ id) (exec (io.run! (atom.update! (|>> (format suffix)) resource)) (waiter resource barrier id)))) (monad.all !)) .let [outcome (io.run! (atom.read! resource))]] (_.cover' [/.barrier /.block!] (and (text.ends_with? expected_ending outcome) (list.every? (function (_ id) (text.contains? (%.nat id) outcome)) expected_ids)))))) ))) (def: .public test Test (<| (_.covering /._) (all _.and ..semaphore ..mutex ..barrier )))