|
1 | 1 | module FSharp.Compiler.Service.Tests.RunCompiler |
2 | 2 |
|
| 3 | +open System |
3 | 4 | open System.Collections.Concurrent |
4 | 5 | open System.Threading |
5 | 6 | open System.Threading.Tasks |
6 | 7 | open NUnit.Framework |
7 | 8 |
|
| 9 | +type Node = |
| 10 | + { |
| 11 | + Idx : int |
| 12 | + Deps : int[] |
| 13 | + Dependants : int[] |
| 14 | + mutable Result : string option |
| 15 | + mutable UnprocessedDepsCount : int |
| 16 | + _lock : Object |
| 17 | + } |
| 18 | + |
8 | 19 | [<Test>] |
9 | 20 | let runCompiler () = |
10 | 21 | // let args = |
11 | 22 | // System.IO.File.ReadAllLines(@"C:\projekty\fsharp\heuristic\tests\FSharp.Compiler.Service.Tests2\args.txt") |> Array.skip 1 |
12 | 23 | // FSharp.Compiler.CommandLineMain.main args |> ignore |
13 | 24 |
|
14 | | - let go (idx : int) = |
15 | | - printfn $"{idx} start" |
16 | | - Thread.Sleep(1000) |
17 | | - printfn $"{idx} stop" |
| 25 | + let fileDeps = |
| 26 | + [| |
| 27 | + 0, [||] // A |
| 28 | + 1, [|0|] // B1 -> A |
| 29 | + 2, [|1|] // B2 -> B1 |
| 30 | + 3, [|0|] // C1 -> A |
| 31 | + 4, [|3|] // C2 -> C1 |
| 32 | + 5, [|2; 4|] // D -> B2, C2 |
| 33 | + |] |
| 34 | + |
| 35 | + let fileDependants = |
| 36 | + fileDeps |
| 37 | + // Collect all edges |
| 38 | + |> Array.collect (fun (idx, deps) -> deps |> Array.map (fun dep -> idx, dep)) |
| 39 | + // Group dependants of the same dependencies together |
| 40 | + |> Array.groupBy (fun (idx, dep) -> dep) |
| 41 | + // Construct reversed graph |
| 42 | + |> Array.map (fun (dep, edges) -> dep, edges |> Array.map fst) |
| 43 | + |> dict |
| 44 | + // Add nodes that are missing due to having no dependants |
| 45 | + |> fun g -> |
| 46 | + fileDeps |
| 47 | + |> Array.map fst |
| 48 | + |> Array.map (fun idx -> |
| 49 | + match g.TryGetValue idx with |
| 50 | + | true, dependants -> dependants |
| 51 | + | false, _ -> [||] |
| 52 | + ) |
| 53 | + |
| 54 | + let graph = |
| 55 | + fileDeps |
| 56 | + |> Seq.map (fun (idx, deps) -> idx, {Idx = idx; Deps = deps; Dependants = fileDependants[idx]; Result = None; UnprocessedDepsCount = deps.Length; _lock = Object()}) |
| 57 | + |> dict |
18 | 58 |
|
19 | 59 | printfn "start" |
20 | 60 | use q = new BlockingCollection<int>() |
21 | | - let work (idx : int) = |
| 61 | + |
| 62 | + // Add leaves to the queue |
| 63 | + let filesWithoutDeps = |
| 64 | + graph |
| 65 | + |> Seq.filter (fun x -> x.Value.UnprocessedDepsCount = 0) |
| 66 | + filesWithoutDeps |
| 67 | + |> Seq.iter (fun f -> q.Add(f.Key)) |
| 68 | + |
| 69 | + // Keep track of the number of items to be processed |
| 70 | + let l = Object() |
| 71 | + let mutable unprocessedCount = graph.Count |
| 72 | + |
| 73 | + let decrementProcessedCount () = |
| 74 | + lock l (fun () -> |
| 75 | + unprocessedCount <- unprocessedCount - 1 |
| 76 | + printfn $"UnprocessedCount = {unprocessedCount}" |
| 77 | + ) |
| 78 | + |
| 79 | + let actualWork (idx : int) = |
| 80 | + idx.ToString() |
| 81 | + |
| 82 | + // Processing of a single node/file - gives a result |
| 83 | + let go (idx : int) = |
| 84 | + let node = graph[idx] |
| 85 | + printfn $"Start {idx} -> %+A{node.Deps}" |
| 86 | + Thread.Sleep(500) |
| 87 | + let res = actualWork idx |
| 88 | + node.Result <- Some res |
| 89 | + printfn $" Stop {idx} work" |
| 90 | + |
| 91 | + // Increment processed deps count for all dependants and schedule those who are now unblocked |
| 92 | + node.Dependants |
| 93 | + |> Array.iter (fun dependant -> |
| 94 | + let node = graph[dependant] |
| 95 | + let unprocessedDepsCount = |
| 96 | + lock node._lock (fun () -> |
| 97 | + node.UnprocessedDepsCount <- node.UnprocessedDepsCount - 1 |
| 98 | + node.UnprocessedDepsCount |
| 99 | + ) |
| 100 | + printfn $"{idx}'s dependant {dependant} now has {unprocessedDepsCount} unprocessed deps left" |
| 101 | + // Dependant is unblocked - schedule it |
| 102 | + if unprocessedDepsCount = 0 then |
| 103 | + printfn $"Scheduling {dependant}" |
| 104 | + q.Add(dependant) |
| 105 | + ) |
| 106 | + |
| 107 | + printfn $"Quitting {idx}" |
| 108 | + decrementProcessedCount () |
| 109 | + () |
| 110 | + |
| 111 | + let workerWork (idx : int) = |
22 | 112 | printfn $"start worker {idx}" |
23 | 113 | q.GetConsumingEnumerable() |
24 | 114 | |> Seq.iter go |
25 | 115 | printfn $"end worker {idx}" |
| 116 | + |
26 | 117 | let maxParallel = 4 |
27 | 118 | printfn "workers" |
28 | 119 | let workers = |
29 | 120 | [|1..maxParallel|] |
30 | | - |> Array.map (fun idx -> Task.Factory.StartNew(fun () -> work idx)) |
| 121 | + |> Array.map (fun idx -> Task.Factory.StartNew(fun () -> workerWork idx)) |
| 122 | + |
| 123 | + while unprocessedCount > 0 do |
| 124 | + Thread.Sleep(100) |
31 | 125 |
|
32 | | - printfn "adding" |
33 | | - for i in [|1..20|] do |
34 | | - q.Add(i) |
35 | | - Thread.Sleep(300) |
36 | 126 | printfn "CompleteAdding" |
37 | 127 | q.CompleteAdding() |
38 | 128 | printfn "waitall" |
|
0 commit comments