2019-12-23 06:32:34 +03:00
|
|
|
# Namespace where tasks can be defined, and hosting methods harmonizing a run.
|
|
|
|
module Tasks
|
|
|
|
# Register a singleton task to be instantiated and ran.
|
|
|
|
# @internal
|
|
|
|
def self.register_singleton(klass)
|
|
|
|
$logger.debug("Task #{klass.name} registered...")
|
|
|
|
@singletons_to_be_instantiated ||= []
|
|
|
|
@singletons_to_be_instantiated << klass
|
|
|
|
end
|
|
|
|
|
|
|
|
# Register one task to be ran.
|
|
|
|
def self.register(task)
|
|
|
|
$logger.debug("Task #{task.name} registered...")
|
|
|
|
@tasks ||= []
|
|
|
|
@tasks << task
|
|
|
|
end
|
|
|
|
|
|
|
|
# Tries to run *all* tasks.
|
|
|
|
def self.go()
|
|
|
|
# Registers tasks that still needs to be instantiated
|
|
|
|
@singletons_to_be_instantiated.each do |klass|
|
2019-12-28 04:38:29 +03:00
|
|
|
# Their mere existence registers them.
|
|
|
|
klass.instance
|
2019-12-23 06:32:34 +03:00
|
|
|
end
|
|
|
|
@singletons_to_be_instantiated = []
|
|
|
|
|
2019-12-28 04:46:04 +03:00
|
|
|
# Sort tasks to reduce the amount of loops it needs to fulfill them all.
|
|
|
|
# It's only a reduction due to files, mounts and devices being
|
|
|
|
# unpredictable!
|
|
|
|
@tasks.sort!
|
|
|
|
|
2019-12-23 06:32:34 +03:00
|
|
|
until @tasks.all?(&:ran) do
|
2020-10-22 07:01:36 +03:00
|
|
|
$logger.debug("=== Tasks resolution loop start ===")
|
2020-10-22 06:52:15 +03:00
|
|
|
ran_one = false
|
2019-12-23 06:32:34 +03:00
|
|
|
@tasks
|
|
|
|
.reject(&:ran)
|
2020-10-22 07:01:36 +03:00
|
|
|
.tap do |tasks|
|
|
|
|
$logger.debug(" Tasks order:")
|
|
|
|
tasks.each do |t|
|
|
|
|
$logger.debug(" - #{t}")
|
|
|
|
end
|
|
|
|
end
|
2019-12-23 06:32:34 +03:00
|
|
|
.each do |task|
|
2020-10-22 06:52:15 +03:00
|
|
|
if task._try_run_task then
|
|
|
|
ran_one = true
|
|
|
|
$logger.debug("#{task} ran.")
|
|
|
|
break
|
|
|
|
end
|
2019-12-23 06:32:34 +03:00
|
|
|
end
|
2020-10-22 06:52:15 +03:00
|
|
|
|
|
|
|
# Don't burn the CPU if we're waiting on something...
|
|
|
|
unless ran_one
|
|
|
|
$logger.debug("Sleeping")
|
|
|
|
sleep(0.1)
|
|
|
|
end
|
2019-12-23 06:32:34 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Basic task class.
|
|
|
|
class Task
|
|
|
|
attr_reader :ran
|
|
|
|
|
|
|
|
def self.new(*args)
|
|
|
|
$logger.debug("New instance of #{self.name}...")
|
|
|
|
$logger.debug(" -> #{args.inspect}")
|
|
|
|
instance = super(*args)
|
|
|
|
Tasks.register(instance)
|
|
|
|
instance
|
|
|
|
end
|
|
|
|
|
|
|
|
def name
|
|
|
|
self.class.name
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.inherited(subclass)
|
|
|
|
$logger.debug("#{subclass.name} created...")
|
|
|
|
end
|
|
|
|
|
2019-12-28 04:46:04 +03:00
|
|
|
# Sort first by dependencies, then by name, then by object_id
|
|
|
|
# (for stable sort order)
|
|
|
|
def <=>(other)
|
|
|
|
return -1 if other.depends_on?(self)
|
|
|
|
return 1 if depends_on?(other)
|
|
|
|
|
2019-12-28 04:56:56 +03:00
|
|
|
by_ux_priority = ux_priority <=> other.ux_priority
|
|
|
|
return by_ux_priority unless by_ux_priority == 0
|
|
|
|
|
2019-12-28 04:46:04 +03:00
|
|
|
by_name = name <=> other.name
|
|
|
|
return by_name unless by_name == 0
|
|
|
|
|
|
|
|
object_id <=> other.object_id
|
|
|
|
end
|
|
|
|
|
|
|
|
def depends_on?(other)
|
|
|
|
dependencies.any? do |dependency|
|
|
|
|
dependency.depends_on?(other)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-23 06:32:34 +03:00
|
|
|
def add_dependency(kind, *args)
|
2019-12-23 07:26:14 +03:00
|
|
|
raise NameError.new("No dependency named #{kind}") unless Dependencies.constants.include?(kind.to_sym)
|
2019-12-23 06:32:34 +03:00
|
|
|
dependencies << Dependencies.const_get(kind.to_sym).new(*args)
|
|
|
|
end
|
|
|
|
|
2019-12-23 22:46:06 +03:00
|
|
|
def dependencies_fulfilled?()
|
|
|
|
dependencies.all?(&:fulfilled?)
|
2019-12-23 06:32:34 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
# Internal actual way to run the task
|
|
|
|
# This runs the `#run` method.
|
2020-10-22 06:52:15 +03:00
|
|
|
# Returns true when the task was ran.
|
2019-12-23 06:32:34 +03:00
|
|
|
def _try_run_task()
|
|
|
|
$logger.debug("Looking to run task #{name}...")
|
2019-12-23 22:46:06 +03:00
|
|
|
return unless dependencies_fulfilled?
|
2019-12-23 06:32:34 +03:00
|
|
|
unless @ran
|
|
|
|
$logger.info("Running #{name}...")
|
|
|
|
run()
|
2019-12-23 22:46:39 +03:00
|
|
|
$logger.debug("Finished #{name}...")
|
2019-12-23 06:32:34 +03:00
|
|
|
@ran = true
|
|
|
|
end
|
2020-10-22 06:52:15 +03:00
|
|
|
|
|
|
|
@ran
|
2019-12-23 06:32:34 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
def dependencies()
|
|
|
|
@dependencies ||= []
|
|
|
|
@dependencies
|
|
|
|
end
|
2019-12-28 04:56:56 +03:00
|
|
|
|
|
|
|
# This allows a task to be ordered before other tasks, because it is used to
|
|
|
|
# enhance the UX of the boot process. Assume this will be compared with +<=>+.
|
|
|
|
# This should seldom be used, and mainly for tasks that show the progress of
|
|
|
|
# the boot process.
|
|
|
|
# (For internal use.)
|
|
|
|
# @internal
|
|
|
|
def ux_priority()
|
|
|
|
0
|
|
|
|
end
|
2020-10-22 06:49:56 +03:00
|
|
|
|
|
|
|
# Same as name, but with the object_id appended.
|
|
|
|
def to_s()
|
|
|
|
name + "<0x#{object_id.to_s(16)}>"
|
|
|
|
end
|
2019-12-23 06:32:34 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
# A task that can only have one instance.
|
|
|
|
class SingletonTask < Task
|
|
|
|
include Singleton
|
|
|
|
|
|
|
|
def self.inherited(subclass)
|
|
|
|
super
|
|
|
|
# Delay initializing, as right now we have an fresh new empty class.
|
|
|
|
Tasks.register_singleton(subclass)
|
|
|
|
end
|
|
|
|
end
|