Flash3388 / FlashLib

A robotics development framework
BSD 3-Clause "New" or "Revised" License
10 stars 0 forks source link

`ConcurrentModificationException` in `SynchronousScheduler` #86

Closed tomtzook closed 1 year ago

tomtzook commented 2 years ago

WhatsApp Image 2022-04-25 at 7 18 52 PM

Encountered during routine usage of the robot code from 2022. The issue can be trace to the loop in SchedulerIteration.runActions which executes actions from the collection mRunningActions in ActionControl.

Such exceptions are caused when modifying a collection while iterating on it with an iterator (which is used in this case). Since the scheduler is only used in one thread, we can assume that somewhere in the loop mentioned above, the collection is modified.

It is likely that one of the executed actions caused a call to Scheduler.cancel which lead to ActionControl.cancelAction which in turn modifies the collection.

tomtzook commented 2 years ago

When making changes you'll have to test to both recreate the issue and see if it is fixed. There are a few ways to do so:

Manual

Since Flashlib in itself is quite generic, you can just run it on any computer. Simply make a test program and run it. You'll be able to see traces from the logger and even use the debugger.

However you must first setup the test program. I recommend taking a look at the examples folder, you'll see several functional programs there.

Just a note about main: Since it's mostly about testing the Scheduler, there's no need to provide any functional HidInterface or IoInterface (which are required), thus you can use the stubs provided. For operation modes, you may use only the disabled mode (will require configuring actions to run in disabled), or some other mode Your main can probably look something like this:

    public static void main(String[] args) {
        final RobotMode RUN_ROBOT = RobotMode.create("run", 1);

        Logger logger = new LoggerBuilder("robot")
                .enableConsoleLogging(true)
                .setLogLevel(LogLevel.DEBUG)
                .build();

        RobotMain.start((l, resourceHolder)-> {
            RobotControl robotControl = new GenericRobotControl(l, resourceHolder,
                    DependencyProvider.cascadingInitializationBuilder(l, resourceHolder)
                    .add(()-> new StaticRobotModeSupplier(RUN_ROBOT))
                    .add(IoInterface.Stub::new)
                    .add(HidInterface.Stub::new)
                    .add(RobotFactory::newDefaultClock)
                    .add((dependencies)-> {
                        Clock clock = dependencies.get(Clock.class);
                        return RobotFactory.newDefaultScheduler(clock, l);
                    })
                    .build());
            RobotBase robotBase = new LoopingRobotBase(Robot::new);

            return new RobotImplementation(robotControl, robotBase);
        }, logger);
    }

Where Robot is the robot class. Write your code in either the constructor, modeInit or modePeriodic.

Automated

FlashLib uses some automated tests, based on junit5. You may add additional tests which check for the bug and so on. However this requires familiarity with writing tests, so it may be more difficult.

If you check here you will see several tests for SchedulerIteration

tomtzook commented 2 years ago

Remember to open a new branch from development to make the changes.

tomtzook commented 2 years ago

Note that this issue will be closed due to the deprecation of ’SynchronousScheduler’ in favor of a new more robust implementation.