sonar

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

live.odin (2757B)


      1 package main
      2 
      3 import "core:fmt"
      4 import "core:sync"
      5 import "core:thread"
      6 import "core:time"
      7 
      8 import "scan"
      9 
     10 /*
     11 Stand in for a UI.
     12 
     13 Three jobs, as the scan is meant to run: a reader filling the tree, a builder owning
     14 everything derived from it, and this loop drawing. The point of the arrangement is
     15 that a frame costs the same whatever the volume holds, because it reads a published
     16 snapshot of twenty rows rather than the tree.
     17 
     18 Which reader fills it is not decided here, and nothing here knows which one did. A
     19 reader settles nodes, and restates if it has to correct itself; that is the whole of
     20 the conversation.
     21 
     22 Nothing here waits on the builder. If a build pass is mid-write the loop draws the
     23 answer from the last one.
     24 */
     25 FRAME :: 16 * time.Millisecond
     26 
     27 run_live :: proc(f: ^Fill) -> int {
     28 	out: Publisher
     29 	started := time.tick_now()
     30 
     31 	f.watched = true
     32 	sync.atomic_store(&f.scanning, true)
     33 
     34 	b := Builder {
     35 		tree     = f.tree,
     36 		out      = &out,
     37 		scanning = &f.scanning,
     38 		started  = started,
     39 	}
     40 
     41 	filler := thread.create_and_start_with_poly_data(f, fill_thread)
     42 	if filler == nil {
     43 		fmt.eprintln("error: could not start the reader")
     44 		return 1
     45 	}
     46 	defer thread.destroy(filler)
     47 
     48 	builder := thread.create_and_start_with_poly_data(&b, build_thread)
     49 	if builder == nil {
     50 		fmt.eprintln("error: could not start the builder")
     51 		return 1
     52 	}
     53 	defer thread.destroy(builder)
     54 	defer rollup_destroy(&b.roll)
     55 
     56 	frames, drawn, missed := 0, 0, 0
     57 	last: Snapshot
     58 	have := false
     59 
     60 	for {
     61 		frame_start := time.tick_now()
     62 		finished := bool(sync.atomic_load(&b.done))
     63 
     64 		if s, ok := current(&out); ok {
     65 			last = s
     66 			have = true
     67 			drawn += 1
     68 		} else {
     69 			missed += 1
     70 		}
     71 
     72 		if have && last.count > 0 {
     73 			fmt.printfln(
     74 				"frame %3d %6.0f ms %8d nodes %6d pending  %10s  %s  frame %5.2f ms%s",
     75 				frames,
     76 				last.elapsed,
     77 				last.nodes,
     78 				last.pending,
     79 				human(last.rows[0].bytes),
     80 				scan.path(f.tree, last.rows[0].node, context.temp_allocator),
     81 				time.duration_milliseconds(time.tick_since(frame_start)),
     82 				last.complete ? "  (final)" : "",
     83 			)
     84 			free_all(context.temp_allocator)
     85 		}
     86 		frames += 1
     87 
     88 		if finished {
     89 			break
     90 		}
     91 
     92 		if spent := time.tick_since(frame_start); spent < FRAME {
     93 			time.sleep(FRAME - spent)
     94 		}
     95 	}
     96 
     97 	thread.join(filler)
     98 	thread.join(builder)
     99 
    100 	if fill_report(f) {
    101 		return 1
    102 	}
    103 
    104 	fmt.println()
    105 	fmt.printfln(
    106 		"scan finished in %.0f ms over %d frames, %d drawn, %d skipped mid-write",
    107 		time.duration_milliseconds(time.tick_since(started)),
    108 		frames,
    109 		drawn,
    110 		missed,
    111 	)
    112 
    113 	print_largest(f.tree, b.roll.totals[:], .files)
    114 	print_largest(f.tree, b.roll.totals[:], .directories)
    115 
    116 	return 0
    117 }
    118 
    119 @(private)
    120 fill_thread :: proc(f: ^Fill) {
    121 	fill_run(f)
    122 }
    123 
    124 @(private)
    125 build_thread :: proc(b: ^Builder) {
    126 	builder_run(b)
    127 }