Skip to content
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
2 changes: 1 addition & 1 deletion src/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class App {
}

/**
* @param {{label: string, delay?: number, repeat?: boolean, errorHandler?: (error: Error, world: World) => void}} config
* @param {{label: string, delay?: number, repeat?: boolean, errorHandler?: (error: Error, world: World) => void, defaultSystemGroup?: import('../type/index.js').Constructor}} config
*/
createSchedule(config) {
return this.scheduler.set(new Executable(config))
Expand Down
9 changes: 8 additions & 1 deletion src/schedule/core/executable.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,26 @@ export class Executable {
*/
delay

/**
* @readonly
* @type {import('../../type/index.js').Constructor | undefined}
*/
defaultSystemGroup

/**
* @readonly
* @type {((error: Error, world: World) => void) | undefined}
*/
errorHandler

/**
* @param {{label: string, repeat?: boolean, delay?: number, errorHandler?: (error: Error, world: World) => void}} config
* @param {{label: string, repeat?: boolean, delay?: number, errorHandler?: (error: Error, world: World) => void, defaultSystemGroup?: import('../../type/index.js').Constructor}} config
*/
constructor(config) {
this.label = config.label
this.repeat = config.repeat ?? true
this.delay = config.delay ?? 0
this.errorHandler = config.errorHandler
this.defaultSystemGroup = config.defaultSystemGroup
}
}
21 changes: 17 additions & 4 deletions src/schedule/core/systembuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,15 @@ export class SchedulerBuilder {
* @param {Scheduler} scheduler
*/
pushToScheduler(scheduler) {
const schedules = this.createScheduleContexts()

/** @type {Map<string, import('./executable.js').Executable['defaultSystemGroup']>} */
const defaultGroupsBySchedule = new Map()

for (const executable of scheduler.values()) {
defaultGroupsBySchedule.set(executable.label, executable.defaultSystemGroup)
}

const schedules = this.createScheduleContexts(defaultGroupsBySchedule)

for (const [scheduleLabel, context] of schedules) {
const schedule = scheduler.get(scheduleLabel)
Expand All @@ -52,9 +60,10 @@ export class SchedulerBuilder {

/**
* @private
* @param {Map<string, Constructor | undefined>} defaultGroupsBySchedule
* @returns {Map<string, ScheduleContext>}
*/
createScheduleContexts() {
createScheduleContexts(defaultGroupsBySchedule) {

/** @type {Map<string, ScheduleContext>} */
const schedules = new Map()
Expand Down Expand Up @@ -115,11 +124,13 @@ export class SchedulerBuilder {
}

for (const [scheduleLabel, context] of schedules) {
context.defaultSystemGroup = defaultGroupsBySchedule.get(scheduleLabel)

this.resolveGroupParents(context, scheduleLabel)

for (let i = 0; i < context.systems.length; i++) {
const system = context.systems[i]
const groupLabel = system.config.systemGroup
const groupLabel = system.config.systemGroup ?? context.defaultSystemGroup

if (!groupLabel) continue

Expand Down Expand Up @@ -433,7 +444,8 @@ function getOrCreateScheduleContext(schedules, label) {
systems: [],
groups: [],
nodesByLabel: new Map(),
groupIdsByTypeId: new Map()
groupIdsByTypeId: new Map(),
defaultSystemGroup: undefined
})

schedules.set(label, created)
Expand Down Expand Up @@ -463,6 +475,7 @@ const ScheduleNodeKind = Object.freeze({
* @property {SystemGroupRegistration[]} groups
* @property {Map<string, ScheduleNodeRef>} nodesByLabel
* @property {Map<TypeId, number>} groupIdsByTypeId
* @property {Constructor | undefined} defaultSystemGroup
*/

/**
Expand Down
100 changes: 100 additions & 0 deletions src/schedule/tests/scheduleBuilder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ class Phase { }
class ParentPhase { }
class ChildPhase { }
class MissingPhase { }
class DefaultPhase { }
class AlternatePhase { }
class FencePhase { }

describe('Testing `SchedulerBuilder`', () => {
test('sorts systems topologically from their `before` and `after` labels', () => {
Expand Down Expand Up @@ -179,6 +182,103 @@ describe('Testing `SchedulerBuilder`', () => {
deepStrictEqual(order, ['prepare', 'first', 'second', 'finish'])
})

test('uses the schedule default system group when a system omits one', () => {
const builder = new SchedulerBuilder()
const scheduler = new Scheduler()
const world = new World()
/** @type {string[]} */
const order = []

function explicit() { order.push('explicit') }
function implicit() { order.push('implicit') }
function other() { order.push('other') }

scheduler.set(new Executable({
label: 'update',
defaultSystemGroup: DefaultPhase
}))

builder.addGroup({
label: DefaultPhase,
schedule: 'update',
before: [AlternatePhase]
})
builder.addGroup({
label: AlternatePhase,
schedule: 'update'
})
builder.add({
schedule: 'update',
systemGroup: DefaultPhase,
system: explicit
})
builder.add({
schedule: 'update',
system: implicit
})
builder.add({
schedule: 'update',
systemGroup: AlternatePhase,
system: other
})

builder.pushToScheduler(scheduler)
scheduler.get('update')?.run(world)

deepStrictEqual(order, ['explicit', 'implicit', 'other'])
})

test('prefers an explicit system group over the schedule default', () => {
const builder = new SchedulerBuilder()
const scheduler = new Scheduler()
const world = new World()
/** @type {string[]} */
const order = []

function explicit() { order.push('explicit') }
function implicit() { order.push('implicit') }
function fence() { order.push('fence') }

scheduler.set(new Executable({
label: 'update',
defaultSystemGroup: DefaultPhase
}))

builder.addGroup({
label: DefaultPhase,
schedule: 'update',
before: [FencePhase]
})
builder.addGroup({
label: AlternatePhase,
schedule: 'update',
after: [FencePhase]
})
builder.addGroup({
label: FencePhase,
schedule: 'update'
})
builder.add({
schedule: 'update',
system: implicit
})
builder.add({
schedule: 'update',
systemGroup: AlternatePhase,
system: explicit
})
builder.add({
schedule: 'update',
systemGroup: FencePhase,
system: fence
})

builder.pushToScheduler(scheduler)
scheduler.get('update')?.run(world)

deepStrictEqual(order, ['implicit', 'fence', 'explicit'])
})

test('orders groups relative to other groups', () => {
const builder = new SchedulerBuilder()
const scheduler = new Scheduler()
Expand Down
Loading