alanmcgovern / monotorrent

The official repository for MonoTorrent, a bittorrent library for .NET

Home Page:https://github.com/alanmcgovern/monotorrent

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using MoveFileAsync multiple times on a file can result in a deadlock

ManlyMarco opened this issue · comments

It seems like file handles are not being closed properly sometimes, but it's difficult to pinpoint what exactly triggers it.

How I'm triggering it:

  1. I'm setting a download directory X for the torrent
  2. run hash check
  3. for all unfinished files: set them as DoNotDownload and use MoveFileAsync to move them to directory Y with random filenames (so that the finished files can keep seeding, but I don't get the 0 byte placeholder files)
  4. start the torrent so it starts seeding
  5. after some time (wait for input from user) some of the unfinished files are set to download
  6. once they finish downloading, I use MoveFileAsync to move them back to directory X to their original paths <- this step can randomly lock up inside MoveFileAsync

Tested on 3.0.0-beta.rev0106.

Does this happen when the number of files in a torrent exceeds MaximumOpenFiles, or does it happen even if MaximumOpenFiles is far higher than the total number of files in all loaded torrents?

I think I can see how this could occur for the latter case. I'll try to get a repro/test for the scenario to confirm whether or not that's actually a cause!

It happens with the default 196 MaximumOpenFiles setting on a 13 file torrent. I didn't test the other way around. I can test builds if you can't reproduce it on your end.

Hrmm, this is really strange. I can't see how you could be triggering this.

after some time (wait for input from user) some of the unfinished files are set to download once they finish downloading, I use MoveFileAsync to move them back to directory X to their original paths <- this step can randomly lock up inside MoveFileAsync

Just to confirm, the version of the library you're using should have this line in TorrentManager.MoveFileAsync:

            if (State != TorrentState.Stopped)
                throw new TorrentException ("Cannot move files when the torrent is active");

This should make it impossible to move files while the engine is running. Is that the case? If so, is the deadlock/hang you're seeing something related to StopAsync ? If so, does the issue still occur if you pass in a timeout, for example TimeSpan.FromSeconds(30) when calling TorrentManager.StopAsync?

Do you have a standalone repro case which I could use to dig into the issue? I've tried removing the exception I linked above from the code and put the engine in a loop where it constantly moves files, and i can't get it to hang, even on a torrent with dozens of files.

This is the version of code I've been using to try and repro the issue:

#604

Yes, I move them only when stopped, the active check is in there. It seems like the key is moving again after a delay. It seems like moving a few times and then starting works fine, but moving, starting, then after downloads finish stopping moving again can trigger this issue. I think I got it to trigger by moving, waiting a while for UI, then moving again (I don't remember if it locked up on the move or after I tried stopping though). Depending on the situation it either locks up on StopAsync or on MoveFileAsync, but unfortunately I didn't take notes so it's a bit of a fuzz to me now (hours of debugging does that to a person).
I'll have to try to reproduce it in a clean project in that case, unless we can talk on discord or something and test on the actual application.

That's enough information to get me started. I haven't been able to reproduce a deadlock/hang between starting/stopping torrents and calling MoveFileAsync, but as long as you're reasonably confident you've seen it lock up on MoveFileAsync i can rule out a funny tracker webrequest timeout related issue and focus exclusively on issues with how those filestreams are handled.

I'll keep you posted! Thanks for the info!

Alright, I've been trying to reproduce this issue and I believe it was a Task deadlock caused by me. After changing some of my async code I can't reproduce it anymore in the application. I couldn't reproduce it in a clean project at all. Either that, or there were some open file handles from somewhere that got cleared out after a restart.

one question - when the deadlock occurs does the app use 100% cpu, or 0%?

0%. I just managed to reproduce the issue by doing a MoveFileAsync.Wait() on main thread while a background thread was waiting in a while loop with await Task.Delay for the MoveFileAsync to finish. For some reason the Wait never completed, but once I changed the code to all be async and awaited the MoveFileAsync, everything started working as expected. Pretty sure this is on me in this case, sorry about that.

Phew! Thanks for the update. I was getting a bit worried about what could be causing it. I was convinced I was missing some simple locking order issue!