builder.odin (2486B)
1 package main 2 3 import "core:sync" 4 import "core:time" 5 6 import "scan" 7 8 /* 9 Owns everything derived from the tree. 10 11 Between the readers and the UI there has to be somewhere the graph work happens. 12 Ancestors are shared between readers, so charging one from several threads would need 13 atomics on every total; giving a single thread sole ownership removes that entirely. 14 The UI is kept out for the opposite reason: its frame must cost the same whatever the 15 volume holds, so it reads a published snapshot rather than the tree. 16 17 Nothing here knows which reader is filling the tree. A reader settles nodes and, if it 18 has to correct itself, restates; those two are the whole of what passes between them. 19 20 This runs as fast as it can rather than on a clock. Each pass charges whatever is newly 21 settled, ranks it, and publishes. If a pass takes longer than a frame the UI simply 22 draws the previous answer. 23 */ 24 Builder :: struct { 25 tree: ^scan.Tree, 26 out: ^Publisher, 27 scanning: ^b32, // cleared once nothing is filling the tree any more 28 started: time.Tick, 29 roll: Rollup, 30 done: b32, 31 } 32 33 builder_run :: proc(b: ^Builder) { 34 // The totals outlive this thread: the caller reads them once the scan is over, 35 // so ownership stays there. 36 defer sync.atomic_store(&b.done, true) 37 38 for { 39 // Read this first: if the scan ends between here and the pass below, the pass 40 // still sees everything and this loop takes one more turn rather than missing 41 // the last of it. 42 finished := !bool(sync.atomic_load(b.scanning)) 43 builder_pass(b, complete = finished) 44 if finished { 45 return 46 } 47 } 48 } 49 50 // One pass: charge what is newly settled, rank it, publish it. 51 @(private = "file") 52 builder_pass :: proc(b: ^Builder, complete: bool) { 53 // A reader that corrects itself invalidates every total drawn from what it said 54 // before, so those start again rather than being added to. It restates only once 55 // the rewrite is finished, so one pass over the tree is enough to replace them. 56 if scan.taken_restated(b.tree) { 57 rollup_restate(&b.roll) 58 } 59 rollup_advance(&b.roll, b.tree) 60 61 top: [TOP_N]Sized 62 count := rank(b.tree, b.roll.totals[:], top[:]) 63 64 s := Snapshot { 65 count = count, 66 nodes = b.tree.nodes_done, 67 bytes = b.tree.bytes_done, 68 pending = b.roll.pending, 69 elapsed = time.duration_milliseconds(time.tick_since(b.started)), 70 complete = complete, 71 } 72 for i in 0 ..< count { 73 s.rows[i] = { 74 node = top[i].record, 75 bytes = top[i].bytes, 76 } 77 } 78 publish(b.out, s) 79 }