• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinaProtocol / mina / 538

25 Aug 2025 05:35PM UTC coverage: 61.202% (+0.4%) from 60.772%
538

push

buildkite

web-flow
Merge pull request #17673 from MinaProtocol/amcie-merge-release320-to-master

amcie-merge-release320-to-master

3142 of 4828 new or added lines in 308 files covered. (65.08%)

205 existing lines in 68 files now uncovered.

50733 of 82894 relevant lines covered (61.2%)

470098.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.0
/src/lib/disk_cache/test/test_cache_deadlock.ml
1
(* Test for cache deadlock with finalizers - works with any disk_cache implementation *)
2

2✔
3
open! Core_kernel
4
open! Async_kernel
5

6
(* Create a custom binable module that triggers GC during serialization *)
7
module Evil_data = struct
NEW
8
  type t = { value : string; trigger_gc : (unit -> unit) option }
×
9
  [@@deriving sexp]
10

11
  (* We convert to/from string for bin_io, injecting our GC trigger during conversion *)
12
  include
13
    Binable.Of_binable_without_uuid
14
      (String)
15
      (struct
16
        type nonrec t = t
17

18
        let to_binable t =
19
          (* Trigger GC during serialization if requested *)
20
          Option.iter t.trigger_gc ~f:(fun f -> f ()) ;
4✔
21
          t.value
8✔
22

NEW
23
        let of_binable value = { value; trigger_gc = None }
×
24
      end)
25
end
26

27
module type Cache_intf = sig
28
  module Make : Disk_cache_intf.F
29
end
30

31
let run_test_with_cache (module Cache_impl : Cache_intf) ~timeout_seconds
32
    ~tmpdir =
33
  let module Cache = Cache_impl.Make (Evil_data) in
2✔
34
  let%bind cache_result = Cache.initialize tmpdir ~logger:(Logger.null ()) in
2✔
35
  let cache =
2✔
36
    match cache_result with
37
    | Ok cache ->
2✔
38
        cache
NEW
39
    | Error (`Initialization_error err) ->
×
NEW
40
        failwithf "Failed to initialize cache: %s" (Error.to_string_hum err) ()
×
41
  in
42

43
  (* Create a cache entry that will be garbage collected when serializing "evil"
44
     data *)
45
  let entry_ref = ref None in
46
  let entry_data = { Evil_data.value = "entry"; trigger_gc = None } in
47
  entry_ref := Some (Cache.put cache entry_data) ;
2✔
48

49
  (* Create evil data that triggers GC during serialization *)
50
  let evil_data =
51
    { Evil_data.value = "evil"
52
    ; trigger_gc =
53
        Some
54
          (fun () ->
55
            (* Clear reference and trigger GC to run finalizers *)
56
            entry_ref := None ;
4✔
57
            Core.printf "References cleared, triggering GC...\n%!" ;
58
            Gc.compact () ;
4✔
59
            Core.printf
4✔
60
              "GC triggered during serialization - finalizers should run now\n\
61
               %!" )
62
    }
63
  in
64

65
  (* This put may deadlock if a finalizer runs during serialization *)
66
  Core.printf "Attempting evil put...\n%!" ;
67

68
  (* Run the potentially deadlocking operation with a timeout *)
69
  let put_with_timeout () =
2✔
70
    let put_deferred =
2✔
71
      Async.In_thread.run (fun () ->
72
          ignore (Cache.put cache evil_data : Cache.id) ;
2✔
73
          `Success )
74
    in
75
    match timeout_seconds with
2✔
76
    | Some timeout ->
2✔
77
        let timeout_span = Core.Time.Span.of_sec timeout in
78
        Async.Clock.with_timeout timeout_span put_deferred
2✔
NEW
79
    | None ->
×
80
        let%map result = put_deferred in
NEW
81
        `Result result
×
82
  in
83

84
  match%bind put_with_timeout () with
2✔
NEW
85
  | `Timeout ->
×
86
      Core.printf
87
        "\nDEADLOCK DETECTED: Cache.put timed out after %.1f seconds!\n"
NEW
88
        (Option.value_exn timeout_seconds) ;
×
NEW
89
      Core.printf "This indicates a deadlock in the cache implementation.\n" ;
×
NEW
90
      Core.printf "The finalizer likely tried to acquire a lock during GC.\n" ;
×
NEW
91
      return `Timeout
×
92
  | `Result `Success ->
2✔
93
      Core.printf "Evil put completed successfully (no deadlock)" ;
94
      Core.printf "\nCache does NOT deadlock with finalizers." ;
2✔
95
      return `Success
2✔
96

97
let test_cache_deadlock (module Cache_impl : Cache_intf) =
98
  (* Read configuration from environment variables *)
99
  let timeout_seconds =
2✔
100
    match Sys.getenv_opt "CACHE_DEADLOCK_TEST_TIMEOUT" with
NEW
101
    | Some "" ->
×
102
        None (* Empty string means no timeout *)
NEW
103
    | Some t ->
×
NEW
104
        Some (Float.of_string t)
×
105
    | None ->
2✔
106
        Some 10.0
107
    (* Default 10 second timeout for CI *)
108
  in
109
  let database_dir = Sys.getenv_opt "CACHE_DEADLOCK_TEST_DIR" in
110

111
  Core.printf "\nCache deadlock test\n" ;
2✔
112
  Core.printf "===================\n" ;
2✔
113
  ( match timeout_seconds with
2✔
114
  | Some t ->
2✔
115
      Core.printf "Timeout: %.1f seconds\n" t
2✔
NEW
116
  | None ->
×
NEW
117
      Core.printf "Timeout: disabled (test will hang if deadlock occurs)\n" ) ;
×
118
  Core.printf "\n" ;
119

120
  let run_in_dir dir =
2✔
121
    Core.printf "Using database directory: %s\n" dir ;
2✔
122
    run_test_with_cache (module Cache_impl) ~timeout_seconds ~tmpdir:dir
2✔
123
  in
124

125
  match database_dir with
NEW
126
  | Some dir ->
×
127
      run_in_dir dir
128
  | None ->
2✔
129
      Mina_stdlib_unix.File_system.with_temp_dir "/tmp/cache_deadlock_test"
130
        ~f:run_in_dir
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc