|  | 
|  | 1 | +(** Sequential and Parallel model-based tests of ws_deque *) | 
|  | 2 | + | 
|  | 3 | +open QCheck | 
|  | 4 | +open STM | 
|  | 5 | +open Util | 
|  | 6 | +module Ws_deque = Lockfree.Ws_deque | 
|  | 7 | + | 
|  | 8 | +module WSDConf = struct | 
|  | 9 | +  type cmd = Push of int | Pop | Steal | 
|  | 10 | + | 
|  | 11 | +  let show_cmd c = | 
|  | 12 | +    match c with | 
|  | 13 | +    | Push i -> "Push " ^ string_of_int i | 
|  | 14 | +    | Pop -> "Pop" | 
|  | 15 | +    | Steal -> "Steal" | 
|  | 16 | + | 
|  | 17 | +  type state = int list | 
|  | 18 | +  type sut = int Ws_deque.M.t | 
|  | 19 | + | 
|  | 20 | +  let arb_cmd _s = | 
|  | 21 | +    let int_gen = Gen.nat in | 
|  | 22 | +    QCheck.make ~print:show_cmd | 
|  | 23 | +      (Gen.oneof | 
|  | 24 | +         [ | 
|  | 25 | +           Gen.map (fun i -> Push i) int_gen; | 
|  | 26 | +           Gen.return Pop; | 
|  | 27 | +           (*Gen.return Steal;*) | 
|  | 28 | +           (* No point in stealing from yourself :-D *) | 
|  | 29 | +         ]) | 
|  | 30 | + | 
|  | 31 | +  let stealer_cmd _s = QCheck.make ~print:show_cmd (Gen.return Steal) | 
|  | 32 | +  let init_state = [] | 
|  | 33 | +  let init_sut () = Ws_deque.M.create () | 
|  | 34 | +  let cleanup _ = () | 
|  | 35 | + | 
|  | 36 | +  let next_state c s = | 
|  | 37 | +    match c with | 
|  | 38 | +    | Push i -> | 
|  | 39 | +        i :: s | 
|  | 40 | +        (*if i<>1213 then i::s else s*) | 
|  | 41 | +        (* an artificial fault *) | 
|  | 42 | +    | Pop -> ( match s with [] -> s | _ :: s' -> s') | 
|  | 43 | +    | Steal -> ( match List.rev s with [] -> s | _ :: s' -> List.rev s') | 
|  | 44 | + | 
|  | 45 | +  let precond _ _ = true | 
|  | 46 | + | 
|  | 47 | +  let run c d = | 
|  | 48 | +    match c with | 
|  | 49 | +    | Push i -> Res (unit, Ws_deque.M.push d i) | 
|  | 50 | +    | Pop -> Res (result int exn, protect Ws_deque.M.pop d) | 
|  | 51 | +    | Steal -> Res (result int exn, protect Ws_deque.M.steal d) | 
|  | 52 | + | 
|  | 53 | +  let postcond c (s : state) res = | 
|  | 54 | +    match (c, res) with | 
|  | 55 | +    | Push _, Res ((Unit, _), _) -> true | 
|  | 56 | +    | Pop, Res ((Result (Int, Exn), _), res) -> ( | 
|  | 57 | +        match s with [] -> res = Error Exit | j :: _ -> res = Ok j) | 
|  | 58 | +    | Steal, Res ((Result (Int, Exn), _), res) -> ( | 
|  | 59 | +        match List.rev s with [] -> Result.is_error res | j :: _ -> res = Ok j) | 
|  | 60 | +    | _, _ -> false | 
|  | 61 | +end | 
|  | 62 | + | 
|  | 63 | +module WSDT_seq = STM_sequential.Make (WSDConf) | 
|  | 64 | +module WSDT_dom = STM_domain.Make (WSDConf) | 
|  | 65 | + | 
|  | 66 | +(* The following definitions differ slightly from those in multicoretests:lib/STM.ml. | 
|  | 67 | +   This has to do with how work-stealing deques are supposed to be used according to spec: | 
|  | 68 | +   - [agree_prop_par] differs in that it only spawns one domain ("a stealer domain") | 
|  | 69 | +     in parallel with the original "owner domain" (it also uses [Semaphore.Binary]) *) | 
|  | 70 | +let agree_prop_par (seq_pref, owner, stealer) = | 
|  | 71 | +  assume (WSDT_seq.cmds_ok WSDConf.init_state (seq_pref @ owner)); | 
|  | 72 | +  assume (WSDT_seq.cmds_ok WSDConf.init_state (seq_pref @ stealer)); | 
|  | 73 | +  let sut = WSDConf.init_sut () in | 
|  | 74 | +  let pref_obs = WSDT_dom.interp_sut_res sut seq_pref in | 
|  | 75 | +  let sema = Semaphore.Binary.make false in | 
|  | 76 | +  let stealer_dom = | 
|  | 77 | +    Domain.spawn (fun () -> | 
|  | 78 | +        Semaphore.Binary.release sema; | 
|  | 79 | +        WSDT_dom.interp_sut_res sut stealer) | 
|  | 80 | +  in | 
|  | 81 | +  while not (Semaphore.Binary.try_acquire sema) do | 
|  | 82 | +    Domain.cpu_relax () | 
|  | 83 | +  done; | 
|  | 84 | +  let own_obs = WSDT_dom.interp_sut_res sut owner in | 
|  | 85 | +  let stealer_obs = Domain.join stealer_dom in | 
|  | 86 | +  let res = | 
|  | 87 | +    WSDT_dom.check_obs pref_obs own_obs stealer_obs WSDConf.init_state | 
|  | 88 | +  in | 
|  | 89 | +  let () = WSDConf.cleanup sut in | 
|  | 90 | +  res | 
|  | 91 | +  || Test.fail_reportf "  Results incompatible with linearized model:\n\n%s" | 
|  | 92 | +     @@ Util.print_triple_vertical ~center_prefix:false STM.show_res | 
|  | 93 | +          (List.map snd pref_obs, List.map snd own_obs, List.map snd stealer_obs) | 
|  | 94 | + | 
|  | 95 | +(* [arb_cmds_par] differs in what each triple component generates: | 
|  | 96 | +   "Owner domain" cmds can't be [Steal], "stealer domain" cmds can only be [Steal]. *) | 
|  | 97 | +let arb_cmds_par = | 
|  | 98 | +  WSDT_dom.arb_triple 20 15 WSDConf.arb_cmd WSDConf.arb_cmd WSDConf.stealer_cmd | 
|  | 99 | + | 
|  | 100 | +(* A parallel agreement test - w/repeat and retries combined *) | 
|  | 101 | +let agree_test_par ~count ~name = | 
|  | 102 | +  let rep_count = 50 in | 
|  | 103 | +  Test.make ~retries:10 ~count ~name arb_cmds_par | 
|  | 104 | +    (repeat rep_count agree_prop_par) | 
|  | 105 | + | 
|  | 106 | +(* Note: this can generate, e.g., pop commands/actions in different threads, thus violating the spec. *) | 
|  | 107 | +let agree_test_par_negative ~count ~name = | 
|  | 108 | +  WSDT_dom.neg_agree_test_par ~count ~name | 
|  | 109 | + | 
|  | 110 | +let () = | 
|  | 111 | +  let count = 1000 in | 
|  | 112 | +  QCheck_base_runner.run_tests_main | 
|  | 113 | +    [ | 
|  | 114 | +      WSDT_seq.agree_test ~count ~name:"STM Lockfree.Ws_deque test sequential"; | 
|  | 115 | +      agree_test_par ~count ~name:"STM Lockfree.Ws_deque test parallel"; | 
|  | 116 | +      agree_test_par_negative ~count | 
|  | 117 | +        ~name:"STM Lockfree.Ws_deque test parallel, negative"; | 
|  | 118 | +    ] | 
0 commit comments