Search code examples
c#iosystem.io.filesharing-violation

Sharing Violation on Path when no stream is open


I'm developing a game in C# in which each "District" will have data about it stored in a save file periodically. For testing, the "SaveAll" method is called once at the beginning of the level.

The code for the file operations is as follows:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.IO;

public class DistrictSaveData : KeepAwake {

    private string saveDirectoryPath = string.Concat(
        System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
        "\\My Games\\District\\Districts");
    private string saveFilePath = string.Concat(
        System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
        "\\My Games\\District\\Districts\\District-x.dat");
    private StreamReader saveFileReader;

    public void SaveAll() {
        foreach (GameObject gO in GameObject.FindGameObjectsWithTag("District")) {
            District district = gO.GetComponent<District>();
            saveFilePath = string.Concat(
                System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
                "\\My Games\\District\\Districts\\District-", district.id , ".dat");
            if (!Directory.Exists(saveDirectoryPath)) {
                Directory.CreateDirectory(saveDirectoryPath);
            }
            try {
            File.Delete(saveFilePath);
            } catch {}
            File.Create(saveFilePath);
            File.WriteAllText(saveFilePath, district.SendSaveData());
        }
    }

    public void LoadAll() {
        foreach (GameObject gO in GameObject.FindGameObjectsWithTag("District")) {
            District district = gO.GetComponent<District>();
            saveFilePath = string.Concat(
                System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
                "\\My Games\\District\\Districts\\District-", district.id , ".dat");
            if(File.Exists(saveFilePath)) {
                OpenFileForReading();
                district.isHQ = bool.Parse(saveFileReader.ReadLine());
                district.controllingFaction = StringToFaction(saveFileReader.ReadLine());
                district.agricultureSpecialisation = StringToAgricultureSpecialisation(saveFileReader.ReadLine());
                district.technologySpecialisation = StringToTechnologySpecialisation(saveFileReader.ReadLine());
                district.militarySpecialisation = StringToMilitarySpecialisation(saveFileReader.ReadLine());
                CloseFileAfterReading();
            } else
                break;
        }
    }

    /// <summary>
    /// Opens the save file for reading.
    /// </summary>
    private void OpenFileForReading() {
        saveFileReader = File.OpenText(saveFilePath);
    }

    /// <summary>
    /// Closes the save file after reading.
    /// </summary>
    private void CloseFileAfterReading() {
        saveFileReader.Close();
    }

    private Faction StringToFaction(string stringToConvert) {
        switch (stringToConvert) {
        case "TheCrimsonLegion":
            return Faction.TheCrimsonLegion;
        case "TheVanguardsOfChaos":
            return Faction.TheVanguardsOfChaos;
        case "TheEmeraldFoxes":
            return Faction.TheEmeraldFoxes;
        case "TheSyndicate":
            return Faction.TheSyndicate;
        case "TheKeepersOfTheTome":
            return Faction.TheKeepersOfTheTome;
        case "TheArchitectsOfThought":
            return Faction.TheArchitectsOfThought;
        default:
            return Faction.None;
        }
    }

    private AgricultureSpecialisation StringToAgricultureSpecialisation(string stringToConvert) {
        switch (stringToConvert) {
        case "Farm":
            return AgricultureSpecialisation.Farm;
        case "Plantation":
            return AgricultureSpecialisation.Plantation;
        case "Biodome":
            return AgricultureSpecialisation.Biodome;
        default:
            return AgricultureSpecialisation.None;
        }
    }

    private TechnologySpecialisation StringToTechnologySpecialisation(string stringToConvert) {
        switch (stringToConvert) {
        case "Laboratory":
            return TechnologySpecialisation.Laboratory;
        case "University":
            return TechnologySpecialisation.University;
        case "GreatTechnologicalInstitution":
            return TechnologySpecialisation.GreatTechnologicalInstitution;
        default:
            return TechnologySpecialisation.None;
        }
    }

    private MilitarySpecialisation StringToMilitarySpecialisation(string stringToConvert) {
        switch (stringToConvert) {
        case "Outpost":
            return MilitarySpecialisation.Outpost;
        case "Barracks":
            return MilitarySpecialisation.Barracks;
        case "Fortress":
            return MilitarySpecialisation.Fortress;
        default:
            return MilitarySpecialisation.None;
        }
    }
}

The exception that is thrown (several times) reads:

IOException: Sharing violation on path C:\users\samdy1\My Documents\My Games\District\Districts\District-0.dat
System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/FileStream.cs:320)
System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
(wrapper remoting-invoke-with-check) System.IO.FileStream:.ctor (string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare,int)
System.IO.File.Create (System.String path, Int32 bufferSize) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/File.cs:135)
System.IO.File.Create (System.String path) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/File.cs:130)
DistrictSaveData+<SaveAll>c__Iterator3.MoveNext () (at Assets/Scripts/_Core/SaveData/DistrictSaveData.cs:29)

This exception is thrown on lines 28 and 29 of the process in the SaveAll method. However, the SaveAll method uses no streams so I fail to see how one can be left open. In fact, at this point in the level, the reading stream hasn't been opened at all.

Have I missed something obvious?


Solution

  • There's not enough information to know for sure, but if you look at this code:

    OpenFileForReading();
    district.isHQ = bool.Parse(saveFileReader.ReadLine());
    district.controllingFaction = StringToFaction(saveFileReader.ReadLine());
    district.agricultureSpecialisation = StringToAgricultureSpecialisation(saveFileReader.ReadLine());
    district.technologySpecialisation = StringToTechnologySpecialisation(saveFileReader.ReadLine());
    district.militarySpecialisation = StringToMilitarySpecialisation(saveFileReader.ReadLine());
    CloseFileAfterReading();
    

    If an exception is thrown after OpenFileForReading() but before CloseFileAfterReading(), the file will not be closed and you will see the behavior your describe.

    At a minimum, rewrite the code as

    OpenFileForReading();
    try
    {
        district.isHQ = bool.Parse(saveFileReader.ReadLine());
        district.controllingFaction = StringToFaction(saveFileReader.ReadLine());
        district.agricultureSpecialisation = StringToAgricultureSpecialisation(saveFileReader.ReadLine());
        district.technologySpecialisation = StringToTechnologySpecialisation(saveFileReader.ReadLine());
        district.militarySpecialisation = StringToMilitarySpecialisation(saveFileReader.ReadLine());
    }
    finally
    {
        CloseFileAfterReading();
    }
    

    You would be better off though with a using block rather than separate method calls to open/close the file.