-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommon.py
More file actions
152 lines (116 loc) · 5.13 KB
/
Copy pathcommon.py
File metadata and controls
152 lines (116 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
Shared demo helpers for desktop and board-specific smallOS examples.
These helpers keep the individual demo files short while still showing the
recommended public API: load a config file, choose a kernel, install an error
handler, spawn tasks, and start the runtime.
"""
from __future__ import annotations
import os
import sys
from typing import Any
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if REPO_ROOT not in sys.path:
sys.path.insert(0, REPO_ROOT)
from SmallPackage.SmallConfig import SmallOSConfig
from SmallPackage.SmallOS import SmallOS
from SmallPackage.SmallTask import SmallTask
from SmallPackage.Kernel import Kernel
CONFIG_PATH = os.path.join(REPO_ROOT, "smallos.config.json")
DEMO_SIGNAL = 3
def load_demo_config(**overrides):
"""Load the repo-level config file and apply any demo-specific overrides."""
config = SmallOSConfig.from_json_file(CONFIG_PATH)
if overrides:
config = config.copy(**overrides)
return config
def build_runtime(kernel: Kernel, **config_overrides: Any) -> SmallOS:
"""Create a ``SmallOS`` instance wired to the chosen kernel."""
runtime = SmallOS(config=load_demo_config(**config_overrides)).setKernel(kernel)
return install_demo_error_handler(runtime)
def task_runtime(task: SmallTask[Any]) -> SmallOS:
"""Return the runtime attached before a registered task is executed."""
runtime = task.OS
if runtime is None:
raise RuntimeError("demo task is not attached to a SmallOS runtime")
return runtime
def _format_failure_event(event):
"""Return a readable multi-line summary for demo task failures."""
details = []
if event["parent_id"] is not None:
details.append("parent={}".format(event["parent_id"]))
if event["blocked_reason"] is not None:
details.append("blocked={}".format(event["blocked_reason"]))
if event["waiting_signal"] is not None:
details.append("signal={}".format(event["waiting_signal"]))
if event["io_wait_mode"] is not None:
details.append("io={}".format(event["io_wait_mode"]))
if event["join_target_id"] is not None:
details.append("join_target={}".format(event["join_target_id"]))
if event["join_pending_ids"]:
details.append("join_pending={}".format(event["join_pending_ids"]))
if event.get("adapter_name") is not None:
details.append(
"adapter={}#{}".format(
event["adapter_name"],
event.get("adapter_job_id"),
)
)
header = "[smallOS demo] task failure"
if event["task_name"]:
header += " in {}".format(event["task_name"])
if event["task_id"] is not None:
header += " (PID {})".format(event["task_id"])
header += ": {}".format(event["exception_repr"])
if details:
header += " [{}]".format(", ".join(details))
trace = event.get("traceback_text")
if trace:
return "{}\n{}".format(header, trace if trace.endswith("\n") else trace + "\n")
return header + "\n"
def install_demo_error_handler(runtime, include_cancelled=False):
"""Attach the shared demo error logger to ``runtime``."""
def _handler(event):
runtime.kernel.write(_format_failure_event(event))
runtime.setErrorHandler(_handler, include_cancelled=include_cancelled)
return runtime
async def worker(task):
"""Simple cooperative child used by several demos."""
for step in range(3):
task.OS.print("[{}] step {}\n".format(task.name, step))
await task.sleep(0.05)
return task.name
async def join_demo(task):
"""Show child spawning plus ordered ``join_all`` collection."""
task.OS.print("join demo starting\n")
fast = task.spawn(worker, priority=1, name="fast")
medium = task.spawn(worker, priority=3, name="medium")
slow = task.spawn(worker, priority=5, name="slow")
results = await task.join_all([fast, medium, slow])
task.OS.print("join demo results: {}\n".format(results))
return results
async def signal_sender(task):
"""Wake the parent after a short delay."""
await task.sleep(0.1)
task.OS.print("sender raising signal {}\n".format(DEMO_SIGNAL))
task.sendSignal(task.parent.pid, DEMO_SIGNAL)
return "signal sent"
async def signal_demo(task):
"""Show a task blocked on a signal and then joined with its sender."""
task.OS.print("signal demo waiting\n")
sender = task.spawn(signal_sender, priority=max(1, task.priority - 1), name="signal_sender")
signal = await task.wait_signal(DEMO_SIGNAL)
sender_result = await task.join(sender)
task.OS.print("signal demo resumed on {} with {}\n".format(signal, sender_result))
return sender_result
async def startup_banner(task, board_name):
"""Print one short startup banner and yield once."""
task.OS.print("smallOS demo booted on {}\n".format(board_name))
await task.yield_now()
return board_name
def default_tasks(board_name):
"""Return a small starter task set used by most demos."""
return [
SmallTask(2, startup_banner, name="startup_banner", args=(board_name,)),
SmallTask(4, signal_demo, name="signal_demo"),
SmallTask(6, join_demo, name="join_demo"),
]