class
Croupier::TaskManagerType
- Croupier::TaskManagerType
- Reference
- Object
Overview
TaskManager is a singleton that keeps track of all tasks
Defined in:
croupier.crConstant Summary
-
STATE_VERSION =
"1" -
Version of the state-file schema, stored as __version. A mismatch (including files written before versioning existed) discards all recorded hashes: one full rebuild instead of silently comparing hashes computed by a different scheme (the directory digest already changed shape once).
Instance Method Summary
-
#_dependencies(outputs : Array(String))
Helper function for dependencies Uses memoization to avoid exponential blowup when many tasks share dependencies
-
#_run_tasks(task_names, run_all : Bool = false, dry_run : Bool = false, keep_going : Bool = false, early_cutoff : Bool = true)
Internal helper to run tasks serially
-
#_run_tasks_parallel(task_names : Array(String) = [] of String, run_all : Bool = false, dry_run : Bool = false, keep_going : Bool = false, early_cutoff : Bool = true)
Internal helper to run tasks concurrently.
- #add_input(task_key : String, input : String) : Bool
- #add_mutex(name : String)
- #all_inputs
-
#auto_mode=(auto_mode : Bool)
If true, it's running in auto mode
-
#auto_mode? : Bool
If true, it's running in auto mode
- #auto_run(targets : Array(String) = [] of String)
- #auto_stop
-
#before_run_hook : Proc(Set(String), Nil)
If set, it's called in auto mode after changes are detected but before tasks run Receives the list of changed files as an argument
-
#before_run_hook=(before_run_hook : Proc(Set(String), Nil))
If set, it's called in auto mode after changes are detected but before tasks run Receives the list of changed files as an argument
-
#check_dependencies(targets : Array(String) | Nil = nil)
Check if all inputs are correct: They should all be either task outputs or existing files.
-
#cleanup
Remove all tasks and everything else (good for tests)
-
#dependencies(outputs : Array(String))
Get a task list of what tasks need to be done to produce
outputsThe list is sorted so it can be executed in order -
#dependencies(output : String)
Get a task list of what tasks need to be done to produce
outputThe list is sorted so it can be executed in order Overloaded to accept a single string for convenience - #depends_on(input : String)
- #depends_on(inputs : Array(String))
-
#early_cutoff=(early_cutoff : Bool)
If true, enable early cutoff optimization (skip tasks when upstream outputs unchanged)
-
#early_cutoff? : Bool
If true, enable early cutoff optimization (skip tasks when upstream outputs unchanged)
-
#fast_dirs=(fast_dirs : Bool)
If true, directories depend on a list of files, not its contents
-
#fast_dirs? : Bool
If true, directories depend on a list of files, not its contents
-
#fast_mode=(fast_mode : Bool)
If true, only compare file dates
-
#fast_mode? : Bool
If true, only compare file dates
-
#file_exists?(path : String) : Bool
File-existence check with a per-run positive cache.
- #get(key)
-
#inputs(targets : Array(String))
The set of all inputs for the given tasks
-
#invalidate_graph_cache
Invalidate the cached task graph.
-
#last_run : Hash(String, String)
SHA1 of files from last run
-
#last_run=(last_run : Hash(String, String))
SHA1 of files from last run
- #lock_mutex(name : String)
-
#mark_stale_inputs(run_all : Bool = false, targets : Array(String) | Nil = nil)
Read state of last run, then scan inputs and compare.
-
#modified : Set(String)
Registry of modified files, which will make tasks stale
-
#modified=(modified : Set(String))
Registry of modified files, which will make tasks stale
-
#modified?(key : String) : Bool
Whether
key(a file or kv:// key) was modified since the last run. -
#mutexes
A hash of mutexes required by tasks
-
#mutexes=(mutexes)
A hash of mutexes required by tasks
-
#next_run : Hash(String, String)
SHA1 of input files as of ending this run
-
#next_run=(next_run : Hash(String, String))
SHA1 of input files as of ending this run
-
#previous_output_hash(output : String) : String | Nil
The hash recorded for
outputby the last completed run, if any. -
#progress_callback : Proc(String, Nil)
If set, it's called after every task finishes
-
#progress_callback=(progress_callback : Proc(String, Nil))
If set, it's called after every task finishes
-
#propagate_staleness(run_all : Bool = false)
Propagate staleness through the task graph in a single forward pass.
-
#record_output_hash(output : String, new_hash : String) : Nil
Record the hash of a task output for the next run's state file.
-
#register_subtask(master_id : String, subtask : Task)
Register a subtask with the task manager
-
#remove_subtasks(master_id : String)
Remove all subtasks belonging to a master task
-
#run_tasks(run_all : Bool = false, dry_run : Bool = false, parallel : Bool = false, keep_going : Bool = false, early_cutoff : Bool | Nil = nil)
Run all stale tasks in dependency order
-
#run_tasks(targets : Array(String), run_all : Bool = false, dry_run : Bool = false, parallel : Bool = false, keep_going : Bool = false, early_cutoff : Bool | Nil = nil)
Run the tasks needed to create or update the requested targets
-
#save_run
We ran all tasks, store the current state.
-
#scan_inputs(scope : Set(String) | Nil = nil)
Scan the given inputs (all of them by default) and return a hash with their sha1.
-
#set(key, value) : Bool
Store a value, returning whether it CHANGED: a same-value set is a no-op for staleness, so kv outputs holding identical values don't re-stale their dependents on every run.
- #sorted_task_graph
-
#state_file : String
Path to the state file that stores hashes between runs
-
#state_file=(state_file : String)
Path to the state file that stores hashes between runs
-
#swap_output_hash(output : String, new_hash : String) : String | Nil
Record
new_hashforoutputand return the hash the last run recorded for it, in a single locked step: task workers call this once per output instead of a record-then-previous round-trip. -
#tasks : Hash(String, Croupier::Task)
Registry of all tasks.
-
#tasks=(tasks : Hash(String, Croupier::Task))
Registry of all tasks.
-
#this_run : Hash(String, String)
SHA1 of files as of starting this run
-
#this_run=(this_run : Hash(String, String))
SHA1 of files as of starting this run
- #unlock_mutex(name : String)
-
#use_persistent_store(path : String)
Use a persistent k/v store in this path instead of the default memory store
- #watch(targets : Array(String) = [] of String)
Instance Method Detail
Helper function for dependencies Uses memoization to avoid exponential blowup when many tasks share dependencies
Internal helper to run tasks serially
Internal helper to run tasks concurrently.
Whenever a task is ready, launch it in a separate fiber. On Crystal >= 1.18 the default execution context is resized to the worker count, so ready tasks run with real multi-core parallelism; on older Crystal this degrades to cooperative concurrency.
Worker fibers only execute tasks and report each outcome over the results channel; this coordinating fiber owns all shared bookkeeping (finished / failed / error collections, stale transitions, early-cutoff notifications), so none of it needs a lock. Receiving batch.size results is the wave barrier. ameba:disable Metrics/CyclomaticComplexity
If set, it's called in auto mode after changes are detected but before tasks run Receives the list of changed files as an argument
If set, it's called in auto mode after changes are detected but before tasks run Receives the list of changed files as an argument
Check if all inputs are correct:
They should all be either task outputs or existing files.
With targets, only the inputs of the requested closure are
checked (a targeted run doesn't care about unrelated tasks'
inputs).
Get a task list of what tasks need to be done to produce outputs
The list is sorted so it can be executed in order
Get a task list of what tasks need to be done to produce output
The list is sorted so it can be executed in order
Overloaded to accept a single string for convenience
If true, enable early cutoff optimization (skip tasks when upstream outputs unchanged)
If true, enable early cutoff optimization (skip tasks when upstream outputs unchanged)
File-existence check with a per-run positive cache. Guarded by @data_mutex like the rest of the shared data.
Invalidate the cached task graph. Only touches in-memory state: @graph_invalidated is what auto_run consults, so there is no reason to round-trip a flag through the k/v store (which, with a persistent store, meant a disk write per invalidation and a disk read per auto cycle).
Read state of last run, then scan inputs and compare.
With run_all the scan only feeds staleness decisions, which are then overridden anyway (every task re-runs), so in fast mode the mtime scan is skipped as pure overhead. Content mode still scans because @this_run feeds save_run and skipping it would make the next incremental run rebuild everything.
Whether key (a file or kv:// key) was modified since the last run.
The hash recorded for output by the last completed run, if any.
If set, it's called after every task finishes
Propagate staleness through the task graph in a single forward pass. This replaces the expensive recursive staleness checking with an O(V+E) algorithm that's critical for tasks with many inputs.
With run_all every task re-runs regardless of freshness, and staleness only gates ordering (dependents wait for stale dependencies). Leaving every task stale preserves correct ordering while skipping the per-task root scan (File.exists? and kv lookups per output), which is pure overhead under run_all.
Record the hash of a task output for the next run's state file. Thread-safe for parallel task workers.
Register a subtask with the task manager
Run all stale tasks in dependency order
If run_all is true, run non-stale tasks too
If dry_run is true, only log what would be done, but don't do it
If parallel is true, run tasks in parallel
If keep_going is true, keep going even if a task fails
If early_cutoff is true, skip tasks when upstream outputs are unchanged
Run the tasks needed to create or update the requested targets
If run_all is true, run non-stale tasks too
If dry_run is true, only log what would be done, but don't do it
If parallel is true, run tasks in parallel
If keep_going is true, keep going even if a task fails
If early_cutoff is true, skip tasks when upstream outputs are unchanged
We ran all tasks, store the current state. Written to a temporary file and renamed into place, so a crash mid-write can't leave a truncated state file behind.
Scan the given inputs (all of them by default) and return a hash with their sha1.
Plain files and the contents of directory inputs are hashed in parallel (a pool of worker fibers bounded by CPU count), since both the disk read and the hashing are independent per file.
Store a value, returning whether it CHANGED: a same-value set is a no-op for staleness, so kv outputs holding identical values don't re-stale their dependents on every run.
Record new_hash for output and return the hash the last run
recorded for it, in a single locked step: task workers call this
once per output instead of a record-then-previous round-trip.
Registry of all tasks.
Treat as read-only while tasks are running: workers and the
coordinating fiber traverse it (and the Task objects in it)
concurrently during run_tasks. To grow a task's dependencies
between runs, use #add_input instead of mutating #tasks or
Task#inputs directly.
Registry of all tasks.
Treat as read-only while tasks are running: workers and the
coordinating fiber traverse it (and the Task objects in it)
concurrently during run_tasks. To grow a task's dependencies
between runs, use #add_input instead of mutating #tasks or
Task#inputs directly.
Use a persistent k/v store in this path instead of the default memory store