In my application several co-routines are downloading files in parallel, and there is a chance that they can try do download the same file to the same path - resulting into a collision and an access exception. I've tried solving this by using a static collection `private static List filesBeingDownloadedNow = new List();` which holds filePath for files which are currently being downloaded.
It looks like this:
private static List filesBeingDownloadedNow = new List();
private IEnumerator DoDownloadCoroutine(string url, string filePath)
{
filesBeingDownloadedNow.Add(filePath);
var webRequest = new UnityWebRequest(url)
{
method = UnityWebRequest.kHttpVerbGET,
downloadHandler = new DownloadHandlerFile(filePath)
{
removeFileOnAbort = true
}
};
yield return webRequest.SendWebRequest();
if (webRequest.isNetworkError || webRequest.isHttpError)
{
// do something if the file failed to download
}
else
{
// do something with the downloaded file
}
filesBeingDownloadedNow.Remove(filePath);
}
and elsewhere:
if (filesBeingDownloadedNow.Contains(filePath))
{
// start another coroutine which simply waits until
// filesBeingDownloadedNow doesn't contain filePath anymore
// and then works with the downloaded file
// as if this coroutine downloaded it itself
}
else
{
StartCoroutine(DoDownloadCoroutine(url, filePath));
}
Now, this works perfectly fine, but there is a rare case where the gameObject on which this coroutine runs can be destroyed before the file download coroutine finishes. So, `yield return webRequest.SendWebRequest();` will be the last thing ever called here, and neither success nor failure can be checked, and `filesBeingDownloadedNow.Remove(filePath);` is never reached.
I tested this, and it seems that `UnityWebRequest.SendWebRequest` still downloads the file in full - however, it's unclear when it will end or if the file gets downloaded successfully at all. It simply "gets lost in the ether".
I could simply trying doing exception handling around File.Open or whatever other things I'd like to do with the downloaded file (and actually should do that in any case). However, UnityWebRequest doesn't allow handling exceptions since it's called with `yield return`.
Even if before calling `webRequest.SendWebRequest();` I check if the file already exists with `if (File.Existst(filePath))`, that doesn't help if 2 different UnityWebRequests try downloading the same file at almost the same time. It can be that the 1st UnityWebRequest has only just connected to the server and hasn't created a file at filePath yet, and the 2nd coroutine will see that the file doesn't exist and assume that no one is downloading it right now, and still attempt a download with `webRequest.SendWebRequest();`, resulting in a collision and an exception which I can't handle.
So basically, there is a combination of factors:
- Co-routines can be silently killed at any moment without anything like "finally" to do clean up or anything before it's killed
- UnityWebRequest.SendWebRequest doesn't allow handling exceptions (?)
- Delay before the downloaded file actually appears at the location makes it impossible to check if a file is already being downloaded
Frankly, I also really dislike the static list here, but it seems that since files are a global resource anyway, making it non-static solves nothing. UnityWebRequest.SendWebRequest will keep downloading the file on its own no matter what I do with that list.
↧