sonar

Scan files at memory bandwidth speed.
Log | Files | Refs

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 }