///|
/// Resource package for M4-FAULT-RESOURCE.
///|
pub fn package_id() -> String {
"lockwire/resource"
}
///|
pub(all) struct ResourceLease {
resource_name : String
amount : Int
granted_at : @core.VTime
} derive(Eq, Debug)
///|
priv struct ResourceWaiter {
amount : Int
event : @scenario.Event[ResourceLease]
}
///|
pub(all) struct ResourceSnapshot {
name : String
capacity : Int
in_use : Int
queue_length : Int
max_queue_length : Int
preemption_count : Int
} derive(Eq, Debug)
///|
pub fn ResourceSnapshot::to_trace_event(
self : ResourceSnapshot,
event_id~ : Int,
vtime~ : @core.VTime,
clock_domain~ : String,
node_id~ : String,
medium_id~ : String,
seed~ : Int,
rng_step~ : Int,
backend~ : @core.BackendProfile,
) -> @trace.TraceEvent {
@trace.TraceEvent::make(
event_id~,
parent_id=None,
vtime~,
clock_domain~,
raw_ns=vtime.ns(),
node_id~,
medium_id~,
channel_id=None,
direction=@trace.Resource,
payload_digest=None,
rng_step~,
seed~,
backend~,
label=self.name,
)
}
///|
pub struct Resource {
priv name : String
priv capacity : Int
priv mut in_use : Int
priv waiters : Array[ResourceWaiter]
priv mut max_queue_length : Int
priv mut preemption_count : Int
}
///|
pub fn Resource::new(name~ : String, capacity~ : Int) -> Resource {
{
name,
capacity: clamp_non_negative(capacity),
in_use: 0,
waiters: [],
max_queue_length: 0,
preemption_count: 0,
}
}
///|
pub fn Resource::name(self : Resource) -> String {
self.name
}
///|
pub fn Resource::capacity(self : Resource) -> Int {
self.capacity
}
///|
pub fn Resource::in_use(self : Resource) -> Int {
self.in_use
}
///|
pub fn Resource::available(self : Resource) -> Int {
self.capacity - self.in_use
}
///|
pub fn Resource::queue_length(self : Resource) -> Int {
self.waiters.length()
}
///|
pub fn Resource::max_queue_length(self : Resource) -> Int {
self.max_queue_length
}
///|
pub fn Resource::preemption_count(self : Resource) -> Int {
self.preemption_count
}
///|
pub fn Resource::request(
self : Resource,
env : @scenario.SimEnv,
amount? : Int = 1,
) -> @scenario.Event[ResourceLease] {
let amount = clamp_non_negative(amount)
let ev : @scenario.Event[ResourceLease] = @scenario.event(env)
if amount <= self.available() {
self.in_use += amount
@scenario.succeed(ev, {
resource_name: self.name,
amount,
granted_at: env.now(),
})
} else {
self.waiters.push({ amount, event: ev })
if self.waiters.length() > self.max_queue_length {
self.max_queue_length = self.waiters.length()
}
}
ev
}
///|
pub fn Resource::release(
self : Resource,
env : @scenario.SimEnv,
lease : ResourceLease,
) -> Unit {
self.in_use = clamp_non_negative(self.in_use - lease.amount)
self.grant_waiters(env)
}
///|
pub fn Resource::preempt(
self : Resource,
env : @scenario.SimEnv,
amount? : Int = 1,
) -> Unit {
self.preemption_count += 1
self.in_use = clamp_non_negative(self.in_use - clamp_non_negative(amount))
self.grant_waiters(env)
}
///|
pub fn Resource::snapshot(self : Resource) -> ResourceSnapshot {
{
name: self.name,
capacity: self.capacity,
in_use: self.in_use,
queue_length: self.queue_length(),
max_queue_length: self.max_queue_length,
preemption_count: self.preemption_count,
}
}
///|
fn Resource::grant_waiters(self : Resource, env : @scenario.SimEnv) -> Unit {
while !self.waiters.is_empty() {
let waiter = self.waiters[0]
if waiter.amount > self.available() {
break
}
ignore(self.waiters.remove(0))
self.in_use += waiter.amount
@scenario.succeed(waiter.event, {
resource_name: self.name,
amount: waiter.amount,
granted_at: env.now(),
})
}
}
///|
pub struct Container {
priv name : String
priv capacity : Int
priv mut level : Int
}
///|
pub fn Container::new(
name~ : String,
capacity~ : Int,
init~ : Int,
) -> Container {
let capacity = clamp_non_negative(capacity)
{ name, capacity, level: clamp_between(init, 0, capacity) }
}
///|
pub fn Container::name(self : Container) -> String {
self.name
}
///|
pub fn Container::capacity(self : Container) -> Int {
self.capacity
}
///|
pub fn Container::level(self : Container) -> Int {
self.level
}
///|
pub fn Container::get(self : Container, amount? : Int = 1) -> Bool {
let amount = clamp_non_negative(amount)
if amount <= self.level {
self.level -= amount
true
} else {
false
}
}
///|
pub fn Container::put(self : Container, amount? : Int = 1) -> Unit {
self.level = clamp_between(
self.level + clamp_non_negative(amount),
0,
self.capacity,
)
}
///|
pub struct Store[T] {
priv name : String
priv capacity : Int
priv items : Array[T]
}
///|
pub fn[T] Store::new(name~ : String, capacity~ : Int) -> Store[T] {
{ name, capacity: clamp_non_negative(capacity), items: [] }
}
///|
pub fn[T] Store::name(self : Store[T]) -> String {
self.name
}
///|
pub fn[T] Store::capacity(self : Store[T]) -> Int {
self.capacity
}
///|
pub fn[T] Store::len(self : Store[T]) -> Int {
self.items.length()
}
///|
pub fn[T] Store::put(self : Store[T], item : T) -> Bool {
if self.items.length() >= self.capacity {
false
} else {
self.items.push(item)
true
}
}
///|
pub fn[T] Store::get(self : Store[T]) -> T? {
if self.items.is_empty() {
None
} else {
Some(self.items.remove(0))
}
}
///|
fn clamp_non_negative(value : Int) -> Int {
if value < 0 {
0
} else {
value
}
}
///|
fn clamp_between(value : Int, min : Int, max : Int) -> Int {
if value < min {
min
} else if value > max {
max
} else {
value
}
}