FileStream on separate Thread seems to block the main thread

When downloading something through my local server in Unity, the editor stops responding until all downloads are complete. How can that happen when it’s running on a separate thread?

Here’s the code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using UnityEngine;

public class LocalServer
{
    public bool runListener = true;
    private string prefix;
    private string baseDirectory = "M:/ftp backup/";
    private volatile int chosenPort;

    public LocalServer(int port)
    {
        prefix = string.Format("http://+:{0}/", port);
        chosenPort = port;
        Thread listenerThread = new Thread(LocalServerThread);
        listenerThread.Start();
    }

    public void LocalServerThread()
    {
        HttpListener listener = new HttpListener();
        listener.Prefixes.Add(prefix);
        listener.Start();
        Debug.Log("<color=green>Started local web server on port: " + chosenPort + "</color>");
        while (runListener)
        {
            IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
            result.AsyncWaitHandle.WaitOne();
        }
        listener.Close();
        Debug.Log("<color=green>Stopped local web server</color>");
    }
public void ListenerCallback(IAsyncResult result)
    {
        HttpListener listener = (HttpListener)result.AsyncState;
        HttpListenerContext context = listener.EndGetContext(result);
        string fileNameComplete = context.Request.Url.AbsolutePath.Substring(1).Replace("%20", " ");
        string pathComplete = baseDirectory + fileNameComplete;
        Debug.Log(pathComplete + " requested to LocalServer");

        using (Stream fileStream = new FileStream(pathComplete, FileMode.Open))
        {
            byte[] buffer = new byte[fileStream.Length];
            fileStream.Read(buffer, 0, (int)fileStream.Length);
            context.Response.ContentLength64 = fileStream.Length;
            context.Response.ContentType = SimpleHTTPServer.GetMimeType(fileNameComplete);
            context.Response.StatusCode = (int)HttpStatusCode.OK;
            context.Response.AddHeader("Cache-Control", "no-cache");
            context.Response.OutputStream.Write(buffer, 0, buffer.Length);
            context.Response.Close();
        }
    }
}

EDIT: When using an external program to download a file from the server running in Unity, Unity does not block. When downloading a file through Unity from an external program that runs the same code, Unity does not block either. So it seems like the combination of uploading and downloading inside Unity actually makes the main Unity thread block. Does someone have an idea why this happens?

EDIT 2: Only DirectShow blocks, MediaFoundation does not, this seems to be a media player related problem, odd that it’s specifically happens when DirectShow downloads to the same application as the one uploading it.

Starting threads while testing inside the editor can be dangerous. You have to make sure that you properly terminate all threads. Stopping playmode does not stop other threads. There are some things a bit strange about your “HTTP server”. First of all your listener thread always waits for the current request to finish. So it can only process one request at a time. You shouldn’t wait for the request to finish in the listener thread as this makes the seperate request threads pointless.
edit
Well actually waiting for the async result of “BeginGetContext” is fine and actually required. Since the handling of the request is handled by another seperate thread. When BeginGetContext “finishes” it means it has started another thread which is handling the request and we can wait for the next request. So this part was actually right.

Currently you encapculated the HttpListener completely inside the “LocalServerThread” method. However you don’t have a reliable way to stop the listener. Setting “runListener” to false will not terminate the thread until another request arrived. You should call Stop on the listener when you want to stop it. In general the listener object should be a member variable of your “LocalServer” class.