Skip to content

Commit

Permalink
first scheduler
Browse files Browse the repository at this point in the history
  • Loading branch information
cnvogelg committed Oct 1, 2024
1 parent 0ca43b8 commit 6cd79d2
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 95 deletions.
1 change: 1 addition & 0 deletions amitools/vamos/cfg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
from .machine import MachineParser
from .proc import ProcessParser
from .profile import ProfileParser
from .schedule import ScheduleParser
from .vamos import VamosMainParser
16 changes: 0 additions & 16 deletions amitools/vamos/cfg/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ def __init__(self, ini_prefix=None):
def_cfg = {
"machine": {
"cpu": Value(str, "68000", enum=cpus),
"max_cycles": 0,
"cycles_per_run": 1000,
"ram_size": 1024,
},
"memmap": {
Expand All @@ -38,18 +36,6 @@ def __init__(self, ini_prefix=None):
action="store",
help="Set type of CPU to emulate (68000, 68020 or 68040)",
),
"max_cycles": Argument(
"--max-cycles",
action="store",
type=int,
help="maximum number of cycles to execute",
),
"cycles_per_run": Argument(
"--cycles-per-block",
action="store",
type=int,
help="cycles per block",
),
"ram_size": Argument(
"-m",
"--ram-size",
Expand All @@ -75,8 +61,6 @@ def __init__(self, ini_prefix=None):
ini_trafo = {
"machine": {
"cpu": "cpu",
"max_cycles": "max_cycles",
"cycles_per_run": "cycles_per_run",
"ram_size": "ram_size",
},
"memmap": {"hw_access": "hw_access", "old_dos_guard": "old_dos_guard"},
Expand Down
35 changes: 35 additions & 0 deletions amitools/vamos/cfg/schedule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from amitools.vamos.cfgcore import *


class ScheduleParser(Parser):
def __init__(self, ini_prefix=None):
def_cfg = {
"schedule": {
"slice_cycles": 1000,
}
}
arg_cfg = {
"schedule": {
"slice_cycles": Argument(
"--slice-cycles",
action="store",
type=int,
help="duration of one one scheduler slice in m68k cycles",
)
}
}
ini_trafo = {
"schedule": {
"slice_cycles": "slice_cycles",
}
}
Parser.__init__(
self,
"schedule",
def_cfg,
arg_cfg,
"schedule",
"scheduler options",
ini_trafo,
ini_prefix,
)
6 changes: 6 additions & 0 deletions amitools/vamos/cfg/vamos.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def __init__(self, debug=None, *args, **kwargs):
# profile
self.profile = ProfileParser()
self.add_parser(self.profile)
# schedule
self.schedule = ScheduleParser()
self.add_parser(self.schedule)

def get_log_dict(self):
return self.log.get_cfg_dict()
Expand All @@ -47,3 +50,6 @@ def get_proc_dict(self):

def get_profile_dict(self):
return self.profile.get_cfg_dict()

def get_schedule_dict(self):
return self.schedule.get_cfg_dict()
2 changes: 2 additions & 0 deletions amitools/vamos/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
log_mem_int = logging.getLogger("mem_int")
log_instr = logging.getLogger("instr")
log_machine = logging.getLogger("machine")
log_schedule = logging.getLogger("schedule")

log_lib = logging.getLogger("lib")
log_libmgr = logging.getLogger("libmgr")
Expand Down Expand Up @@ -56,6 +57,7 @@
log_hw,
log_math,
log_machine,
log_schedule,
]

preset = {log_prof: logging.INFO}
Expand Down
7 changes: 5 additions & 2 deletions amitools/vamos/machine/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@ def reset_slice(self):
"""allow to reset slice reporting"""
self.left_cycles = self.slice_cycles

def run(self, pc, sp, set_regs=None, get_regs=None, name=None) -> RunState:
def is_running(self):
return self.running

def run(self, pc, sp=None, set_regs=None, get_regs=None, name=None) -> RunState:
"""convenience method to dispatch either to start or nested_run"""
if not self.running:
return self.start(pc, sp, set_regs, get_regs, name)
else:
return self.nested_run(pc, sp, set_regs, get_regs, name)

def start(self, pc, sp, set_regs=None, get_regs=None, name=None) -> RunState:
def start(self, pc, sp=None, set_regs=None, get_regs=None, name=None) -> RunState:
"""start the main run at given pc with stack and return result"""
if self.running:
raise RuntimeError("start() only allowed inside idle runtime.")
Expand Down
17 changes: 8 additions & 9 deletions amitools/vamos/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ def main(cfg_files=None, args=None, cfg_dict=None, profile=False):
return RET_CODE_CONFIG_ERROR

# setup scheduler
scheduler = Scheduler(machine)
schedule_cfg = mp.get_schedule_dict().schedule
scheduler = Scheduler.from_cfg(machine, schedule_cfg)

# a default runtime for m68k code execution after scheduling
default_runtime = Runtime(machine, machine.scratch_end)
Expand Down Expand Up @@ -118,20 +119,18 @@ def runner(*args, **kw_args):
log_main.error("main proc setup failed!")
return RET_CODE_CONFIG_ERROR

# main loop
# add main task
task = main_proc.get_task()
scheduler.add_task(task)
scheduler.schedule()

# check proc result
run_state = task.get_result()
# main loop
scheduler.schedule()

# return code is limited to 0-255
exit_code = run_state.regs[REG_D0] & 0xFF
exit_code = task.get_exit_code() & 0xFF
run_state = task.get_run_result()
log_main.info("done. exit code=%d", exit_code)
log_main.info(
"cycles: main=%d total=%d", run_state.cycles, run_state.total_cycles
)
log_main.debug("run result: %r", run_state)

# shutdown main proc
main_proc.free()
Expand Down
149 changes: 121 additions & 28 deletions amitools/vamos/schedule/scheduler.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import greenlet

from amitools.vamos.log import log_schedule
from amitools.vamos.schedule.task import TaskState


class Scheduler(object):
"""handle the execution of multiple tasks"""

def __init__(self, machine):
def __init__(self, machine, slice_cycles=1000):
self.machine = machine
self.tasks = []
self.slice_cycles = slice_cycles
# state
self.added_tasks = []
self.ready_tasks = []
self.waiting_tasks = []
self.cur_task_hook = None
self.cur_task = None
self.num_tasks = 0
self.main_glet = greenlet.getcurrent()
self.num_switch_same = 0
self.num_switch_other = 0

@classmethod
def from_cfg(cls, machine, schedule_cfg):
return cls(machine, schedule_cfg.slice_cycles)

def get_machine(self):
return self.machine
Expand All @@ -14,56 +32,131 @@ def set_cur_task_callback(self, func):
self.cur_task_hook = func

def get_num_tasks(self):
return len(self.tasks)
"""count the active tasks"""
sum = len(self.ready_tasks) + len(self.waiting_tasks) + len(self.added_tasks)
if self.cur_task:
sum += 1
return sum

def get_cur_task(self):
return self.cur_task

def schedule(self):
"""main work call for scheduler. at least one task must be added.
terminates if there are no more tasks to schedule or if a task
failed.
return result of last task
fails and an uncaught exception is thrown.
"""
log_schedule.info("schedule(): start")

# check that we have at least one task to run
if len(self.tasks) == 0:
if len(self.added_tasks) == 0:
raise RuntimeError("no tasks to schedule!")

# currently we are single task
# so for now simply run a single task
task = self.tasks[0]
# main loop
while True:
# nothing to do anymore?
if self.get_num_tasks() == 0:
break

# find a task to run
task = self._find_run_task()
if task is None:
log_schedule.error("schedule(): no task to run?!")
return False

# current tasks stays the same?
# no context switch required. simply switch to it
if task == self.cur_task:
self.num_switch_same += 1
log_schedule.debug("run: current %s", task.name)
task.switch()
else:
self.num_switch_other += 1
# switch out old
old_task = self.cur_task
if old_task:
log_schedule.debug("run: switch out %s", old_task.name)
old_task.set_state(TaskState.TS_READY)
self.ready_tasks.append(old_task)

old_task.save_ctx()

# switch in new
self.cur_task = task
self._make_current(task)
task.set_state(TaskState.TS_RUN)
log_schedule.debug("run: switch in %s", task.name)

task.restore_ctx()
task.switch()

self._make_current(None)
log_schedule.info(
"schedule(): done (switches: same=%d, other=%d)",
self.num_switch_same,
self.num_switch_other,
)
return True

# report this task
def _find_run_task(self):
# if added tasks are available take this one
if len(self.added_tasks) > 0:
task = self.added_tasks.pop(0)
log_schedule.debug("take: added task %s", task.name)
return task

# if a ready task is available
if len(self.ready_tasks):
task = self.ready_tasks.pop(0)
log_schedule.debug("take: ready task %s", task.name)
return task

# keep current task
task = self.cur_task
log_schedule.debug("take: current task %s", task.name)
return task

def _make_current(self, task):
self.cur_task = task
if self.cur_task_hook:
self.cur_task_hook(task)

# start task
task.start()

# cleanup task
task.free()

# no more cur task
self.cur_task = None
if self.cur_task_hook:
self.cur_task_hook(None)

def add_task(self, task):
"""add a new task and prepare for execution.
returns True if task was added
"""
self.tasks.append(task)
# assign myself
task.set_scheduler(self)
# return regs
self.added_tasks.append(task)
task.set_state(TaskState.TS_ADDED)
# configure task
task.config(self, self.slice_cycles)
log_schedule.info("add_task: %s", task.name)
return True

def rem_task(self, task):
raise NotImplementedError
# find task: is it current?
if self.cur_task == task:
self.cur_task = None
# in ready list?
elif task in self.ready_tasks:
self.ready_tasks.remove(task)
# in waiting list?
elif task in self.waiting_tasks:
self.waiting_tasks.remove(task)
# in added list?
elif task in self.added_tasks:
self.added_tasks.remove(task)
# not found
else:
log_schedule.warn("rem_task: unknown task %s", task.name)
return False
# mark as removed
task.set_state(TaskState.TS_REMOVED)
log_schedule.info("rem_task: %s", task.name)
# finally free task
task.free()
return True

def reschedule(self, task):
"""callback from tasks to reschedule"""
pass
self.main_glet.switch()
Loading

0 comments on commit 6cd79d2

Please sign in to comment.