-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[core] Add max iteration count for detecting infinite loops #5430
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
base: main
Are you sure you want to change the base?
Changes from 6 commits
8996022
318dc54
792bc8a
8fecaac
5c23e39
e500583
451f682
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| 'xstate': minor | ||
| --- | ||
|
|
||
| Add `maxIterations` option to configure the maximum number of microsteps allowed before throwing an infinite loop error. The default is `Infinity` (no limit) to avoid breaking existing machines. | ||
|
|
||
| You can configure it when creating a machine: | ||
|
|
||
| ```ts | ||
| const machine = createMachine({ | ||
| // ... machine config | ||
| options: { | ||
| maxIterations: 1000 // set a limit to enable infinite loop detection | ||
| } | ||
| }); | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -566,6 +566,81 @@ describe('transient states (eventless transitions)', () => { | |
| service.send({ type: 'EVENT', value: 42 }); | ||
| }); | ||
|
|
||
| it('should avoid infinite loops with eventless transitions', () => { | ||
| expect.assertions(1); | ||
| const machine = createMachine({ | ||
| initial: 'a', | ||
| options: { | ||
| maxIterations: 100 | ||
| }, | ||
| states: { | ||
| a: { | ||
| always: { | ||
| target: 'b' | ||
| } | ||
| }, | ||
| b: { | ||
| always: { | ||
| target: 'c' | ||
| } | ||
| }, | ||
| c: { | ||
| always: { | ||
| target: 'a' | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| const actor = createActor(machine); | ||
|
|
||
| actor.subscribe({ | ||
| error: (err) => { | ||
| expect((err as any).message).toMatch(/infinite loop/i); | ||
| } | ||
| }); | ||
|
|
||
| actor.start(); | ||
| }); | ||
|
|
||
| it('should avoid infinite loops with raised events', () => { | ||
| expect.assertions(1); | ||
| const machine = createMachine({ | ||
| initial: 'a', | ||
| states: { | ||
| a: { | ||
| always: { | ||
| target: 'b' | ||
| } | ||
| }, | ||
| b: { | ||
| entry: raise({ type: 'EVENT' }), | ||
| on: { | ||
| EVENT: { | ||
| target: 'c' | ||
| } | ||
| } | ||
| }, | ||
| c: { | ||
| always: { | ||
| target: 'a' | ||
| } | ||
| } | ||
| }, | ||
| options: { | ||
| maxIterations: 100 | ||
| } | ||
|
Comment on lines
+629
to
+631
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's yet another place when some options are accepted. I'm not saying this is inherently bad but I can see how this might not be that discoverable given this is the only option accepted this way now.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also wonder, shouldn't this kinda be a global option really? As a user, I don't see myself using this everywhere - and currently I'd have to because it's opt-in. Some of other libraries (like even React) just implements such protections automatically and they don't allow the user to confgure the "depth count" that triggers the error. |
||
| }); | ||
| const actor = createActor(machine); | ||
|
|
||
| actor.subscribe({ | ||
| error: (err) => { | ||
| expect((err as any).message).toMatch(/infinite loop/i); | ||
| } | ||
| }); | ||
|
|
||
| actor.start(); | ||
| }); | ||
|
|
||
| it("shouldn't end up in an infinite loop when selecting the fallback target", () => { | ||
| const machine = createMachine({ | ||
| initial: 'idle', | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.