Skip to content

Allow access to the list of created resources. #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.0

* Access the list of created resources.

## 0.2.0

* Setting to determine how often `Future.delayed` is used instead of `Future.microtask` during event execution to allow GUI refresh.
Expand All @@ -8,8 +12,8 @@
## 0.1.0

* Initial release
* Discrete event processing
* Event scheduling, execution, waiting, and repetition
* Intervals management
* Discrete event processing.
* Event scheduling, execution, waiting, and repetition.
* Intervals management.
* Resources
* Capacity limit
2 changes: 1 addition & 1 deletion lib/simdart.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export 'src/event.dart';
export 'src/interval.dart';
export 'src/observable.dart';
export 'src/resources.dart' hide ResourcesFactory;
export 'src/resources.dart' hide ResourcesFactory, ResourceStore;
export 'src/simdart.dart' hide SimDartHelper;
export 'src/simulation_track.dart';
export 'src/start_time_handling.dart';
Expand Down
50 changes: 0 additions & 50 deletions lib/src/internal/resource.dart

This file was deleted.

138 changes: 114 additions & 24 deletions lib/src/resources.dart
Original file line number Diff line number Diff line change
@@ -1,36 +1,126 @@
import 'dart:collection';

import 'package:meta/meta.dart';
import 'package:simdart/src/internal/completer_action.dart';
import 'package:simdart/src/internal/event_action.dart';
import 'package:simdart/src/internal/resource.dart';
import 'package:simdart/src/simdart.dart';
import 'package:simdart/src/simulation_track.dart';

@internal
class ResourceStore {
/// Holds the resources in the simulator.
final Map<String, _ResourceImpl> _map = {};
final List<_ResourceImpl> _list = [];
late final UnmodifiableListView<_ResourceImpl> _unmodifiableList =
UnmodifiableListView(_list);

Map<String, int> usage() {
Map<String, int> result = {};
for (_ResourceImpl resource in _map.values) {
result[resource.name] = resource._queue.length;
}
return result;
}
}

abstract interface class Resource {
String get name;
int get capacity;
bool isAvailable();
}

abstract interface class LimitedResource extends Resource {}

abstract class _ResourceImpl implements Resource {
_ResourceImpl(
{required this.name,
required this.capacity,
required this.acquisitionRule});

@override
final String name;

@override
final int capacity;

final List<EventAction> _queue = [];
final bool Function(EventAction event)? acquisitionRule;

/// A queue that holds completer to resume events waiting for a resource to become available.
final List<EventAction> _waiting = [];

bool acquire(EventAction event);

bool release(SimDart sim, EventAction event);
}

class _LimitedResourceImpl extends _ResourceImpl implements LimitedResource {
_LimitedResourceImpl(
{required super.name,
required super.capacity,
required super.acquisitionRule});

@override
bool acquire(EventAction event) {
if (acquisitionRule != null && !acquisitionRule!(event)) {
return false;
}
if (isAvailable()) {
_queue.add(event);
return true;
}

return false;
}

@override
bool release(SimDart sim, EventAction event) {
return _queue.remove(event);
}

@override
bool isAvailable() {
return _queue.length < capacity;
}
}

class Resources {
Resources._(SimDart sim) : _sim = sim;
Resources._(SimDart sim)
: _sim = sim,
_store = SimDartHelper.resourceStore(sim: sim);

final SimDart _sim;
final ResourceStore _store;

List<Resource> get all => _store._unmodifiableList;

int get length => _store._map.length;

/// Creates a resource with limited capacity.
///
/// This method adds a resource with the specified capacity.
/// The resource will be configured as limited, meaning it will have a maximum
/// capacity defined by the [capacity] parameter.
///
/// - [id]: The unique identifier of the resource (required).
/// - [name]: The unique name of the resource (required).
/// - [capacity]: The maximum capacity of the resource. The default value is 1.
void limited({required String id, int capacity = 1}) {
SimDartHelper.addResource(
sim: _sim,
resourceId: id,
create: () => LimitedResource(id: id, capacity: capacity));
Resource limited({required String name, int capacity = 1}) {
_ResourceImpl? resource = _store._map[name];
if (resource == null) {
resource = _LimitedResourceImpl(
name: name, capacity: capacity, acquisitionRule: null);
_store._map[name] = resource;
_store._list.add(resource);
}
return resource;
}

/// Checks if a resource is available.
///
/// - [id]: The id of the resource to check.
/// - [name]: The name of the resource to check.
/// - Returns: `true` if the resource is available, `false` otherwise.
bool isAvailable(String id) {
Resource? resource = SimDartHelper.getResource(sim: _sim, resourceId: id);
bool isAvailable(String name) {
_ResourceImpl? resource = _store._map[name];
if (resource != null) {
return resource.isAvailable();
}
Expand All @@ -47,10 +137,10 @@ class ResourcesContext extends Resources {

/// Tries to acquire a resource immediately.
///
/// - [id]: The id of the resource to acquire.
/// - [name]: The name of the resource to acquire.
/// - Returns: `true` if the resource was acquired, `false` otherwise.
bool tryAcquire(String id) {
Resource? resource = SimDartHelper.getResource(sim: _sim, resourceId: id);
bool tryAcquire(String name) {
_ResourceImpl? resource = _store._map[name];
if (resource != null) {
return resource.acquire(_event);
}
Expand All @@ -59,17 +149,17 @@ class ResourcesContext extends Resources {

/// Acquires a resource, waiting if necessary until it becomes available.
///
/// - [id]: The id of the resource to acquire.
/// - [name]: The name of the resource to acquire.
/// - Returns: A [Future] that completes when the resource is acquired.
Future<void> acquire(String id) async {
Future<void> acquire(String name) async {
if (_event.eventCompleter != null) {
SimDartHelper.error(
sim: _sim,
msg:
"This event should be waiting for the resource to be released. Did you forget to use 'await'?");
return;
}
Resource? resource = SimDartHelper.getResource(sim: _sim, resourceId: id);
_ResourceImpl? resource = _store._map[name];
if (resource != null) {
bool acquired = resource.acquire(_event);
if (!acquired) {
Expand All @@ -78,23 +168,23 @@ class ResourcesContext extends Resources {
sim: _sim, eventName: _event.eventName, status: Status.yielded);
}
_event.buildCompleter();
resource.waiting.add(_event);
resource._waiting.add(_event);
SimDartHelper.scheduleNextAction(sim: _sim);
await _event.eventCompleter!.future;
return await acquire(id);
return await acquire(name);
}
}
}

/// Releases a previously acquired resource.
///
/// - [id]: The id of the resource to release.
void release(String id) {
Resource? resource = SimDartHelper.getResource(sim: _sim, resourceId: id);
/// - [name]: The name of the resource to release.
void release(String name) {
_ResourceImpl? resource = _store._map[name];
if (resource != null) {
if (resource.release(_sim, _event)) {
if (resource.waiting.isNotEmpty) {
EventAction other = resource.waiting.removeAt(0);
if (resource._waiting.isNotEmpty) {
EventAction other = resource._waiting.removeAt(0);
// Schedule a complete to resume this event in the future.
SimDartHelper.addAction(
sim: _sim,
Expand Down
24 changes: 4 additions & 20 deletions lib/src/simdart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:meta/meta.dart';
import 'package:simdart/src/event.dart';
import 'package:simdart/src/internal/event_action.dart';
import 'package:simdart/src/internal/repeat_event_action.dart';
import 'package:simdart/src/internal/resource.dart';
import 'package:simdart/src/internal/simdart_interface.dart';
import 'package:simdart/src/internal/time_action.dart';
import 'package:simdart/src/interval.dart';
Expand Down Expand Up @@ -51,9 +50,7 @@ class SimDart implements SimDartInterface {
final Map<String, SimNum> _numProperties = {};
final Map<String, SimCounter> _counterProperties = {};

/// Holds the resources in the simulator.
final Map<String, Resource> _resources = {};

final ResourceStore _resourceStore = ResourceStore();
late final Resources resources = ResourcesFactory.sim(this);

/// The instance of the random number generator used across the simulation.
Expand Down Expand Up @@ -242,16 +239,12 @@ class SimDart implements SimDartInterface {
}

void _addTrack({required String eventName, required Status status}) {
Map<String, int> resourceUsage = {};
for (Resource resource in _resources.values) {
resourceUsage[resource.id] = resource.queue.length;
}
_tracks ??= [];
_tracks!.add(SimulationTrack(
status: status,
name: eventName,
time: now,
resourceUsage: resourceUsage));
resourceUsage: _resourceStore.usage()));
}

Future<void> _consumeNextAction() async {
Expand Down Expand Up @@ -296,17 +289,8 @@ class SimDartHelper {
sim._addAction(action);
}

static Resource? getResource(
{required SimDart sim, required String? resourceId}) {
return sim._resources[resourceId];
}

static void addResource(
{required SimDart sim,
required String resourceId,
required Resource Function() create}) {
sim._resources.putIfAbsent(resourceId, create);
}
static ResourceStore resourceStore({required SimDart sim}) =>
sim._resourceStore;

static void addSimulationTrack(
{required SimDart sim,
Expand Down
6 changes: 3 additions & 3 deletions test/repeat_process_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void main() {
test('Resource - acquire and wait', () async {
SimDart sim = SimDart(includeTracks: true);

sim.resources.limited(id: 'r');
sim.resources.limited(name: 'r');

sim.process(
event: (context) async {
Expand Down Expand Up @@ -79,7 +79,7 @@ void main() {
test('Resource', () async {
SimDart sim = SimDart(includeTracks: true);

sim.resources.limited(id: 'r');
sim.resources.limited(name: 'r');

sim.repeatProcess(
event: (context) async {
Expand Down Expand Up @@ -113,7 +113,7 @@ void main() {
test('Resource - stop', () async {
SimDart sim = SimDart(includeTracks: true);

sim.resources.limited(id: 'r');
sim.resources.limited(name: 'r');

sim.repeatProcess(
event: (context) async {
Expand Down
Loading