// NOTE: Put your solution here in RobotSimulation class - document your solution in comments in code + in RemovedDataRacesLog.md. // NOTE: Fix the concurrency bugs in the code directly (i.e. add new fields/properties and modify existing methods), you can // add new methods as well, as long as you add a call to these methods to the existing code // IMPORTANT NOTE: Your solution must compile without error, and should not contain any data races! // IMPORTANT NOTE: Your solution should be correct on every platform in regard to ANY possible thread scheduling done by the OS scheduler! // IMPORTANT NOTE: Do not refactor the solution, do not change the processing algorithm, only modify the places in the code containing data races. // The solution should still contain SimulateOneStep and DetermineNewLocation methods. //-------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // Updated by Pavel Jezek, Charles University in Prague // Modernized by Filip Kliber, Charles University // // File: RoomSimulation.cs // //-------------------------------------------------------------------------- namespace AntisocialRobots; public class RobotSimulation : RobotSimulationBase { private object[,] _roomLocks; public RobotSimulation(int size) : base(size) { _roomLocks = new object[size, size]; for (int i = 0; i < size; i++) for (int j = 0; j < size; ++j) _roomLocks[i, j] = new(); } public enum FrameUpdateMode { Sequential = 0, Parallel } public FrameUpdateMode UpdateMode { get; set; } protected override void OnPerformFrameUpdate(int frameIdx) { switch (UpdateMode) { case FrameUpdateMode.Sequential: FrameUpdate_Sequential(); break; case FrameUpdateMode.Parallel: FrameUpdate_Parallel(SimulateOneStep); break; } } private void FrameUpdate_Sequential() { Action simulateOneStep = SimulateOneStep; foreach (Robot robot in _movableRobots) simulateOneStep(robot); } private void FrameUpdate_Parallel(Action simulateOneStep) { int threadCount = Environment.ProcessorCount; if (_movableRobots.Count < threadCount) { FrameUpdate_Sequential(); return; } int robotsPerThread = _movableRobots.Count / threadCount; int robotsUnassigned = _movableRobots.Count % threadCount; Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threads.Length; i++) { int from = i * robotsPerThread; int to = from + robotsPerThread - 1; if (i == threads.Length - 1) { to += robotsUnassigned; } threads[i] = new Thread(() => { for (int ri = from; ri <= to; ri++) { simulateOneStep(_movableRobots[ri]); } }); threads[i].Start(); } foreach (var t in threads) { t.Join(); } } /// Performs one step of the simulation for one robot. /// The robot to perform the simulation step for. private void SimulateOneStep(Robot r) { if (!r.IsMovable) return; RoomPoint origLoc = r.Location; RoomPoint newLoc = DetermineNewLocation(r); if (!newLoc.Equals(origLoc)) { lock (_roomLocks[newLoc.X, newLoc.Y]) { if (_roomCells[newLoc.X, newLoc.Y] == null) { _roomCells[origLoc.X, origLoc.Y] = null; _roomCells[newLoc.X, newLoc.Y] = r; lock (r) r.Location = newLoc; } } } } /// /// Computes the new desired location for the robot. /// This version doesn't do any thread synchronization /// operations, and is therefore NOT thread-safe. /// /// The robot to determine new location for. /// New location for the robot. private RoomPoint DetermineNewLocation(Robot r) { RoomPoint ptR = r.Location; double vectorX = 0, vectorY = 0; foreach (Robot s in _robots) { if (r == s) continue; // skip ourselves RoomPoint ptS; lock (s) ptS = s.Location; double inverseSquareDistance = 1.0 / RoomPoint.Square(ptR.DistanceTo(ptS)); double angle = ptR.AngleTo(ptS); vectorX -= inverseSquareDistance * Math.Cos(angle); vectorY -= inverseSquareDistance * Math.Sin(angle); } return ComputeNewLocation(ptR, vectorX, vectorY, ROOM_SIZE); } }