2016-08-04 2 views
0

У меня есть сервер, который запускает TcpListener для прослушивания входящих соединений на указанный порт. Это осуществляется следующим образом:System.AppDomainUnloadedException в тесте из-нить не останавливая

type TCPListenerServer(discoveryPort:int) = 
    let server = new TcpListener (IPAddress.Loopback, discoveryPort) 

    let activeConnections = new List<TcpClient>() 
    let cancellationToken = new System.Threading.CancellationTokenSource() 

    let rec loop (pendingConnection:Task<TcpClient>) = async {    
     let newPendingConnection, client = 
      match pendingConnection.Status with 
      | TaskStatus.Created | TaskStatus.WaitingForActivation | TaskStatus.WaitingToRun 
      | TaskStatus.WaitingForChildrenToComplete | TaskStatus.Running -> 
       (None, None) 
      | TaskStatus.Faulted -> 
       let result = pendingConnection.Exception 
       raise (new System.NotImplementedException()) 
      | TaskStatus.Canceled -> 
       raise (new System.NotImplementedException()) 
      | TaskStatus.RanToCompletion -> 
       let connectionTask = server.AcceptTcpClientAsync() 
       (Some connectionTask, Some pendingConnection.Result) 
      | _ -> 
       raise (new System.NotImplementedException()) 

     // Add the new client to the list 
     Option.iter (fun c -> activeConnections.Add c) client 

     // Switch the new pending connection if there is one 
     let connectionAttempt = defaultArg newPendingConnection pendingConnection 

     // Check that the connections are still alive 
     Seq.iter (fun (connection:TcpClient) -> if not connection.Connected then activeConnections.Remove connection |> ignore) activeConnections 

     Async.Sleep 1000 |> Async.RunSynchronously 
     return! loop connectionAttempt 
    } 

    member x.Start() = 
     try 
      server.Start() 
      let connectionTask = server.AcceptTcpClientAsync() 
      Async.Start (loop connectionTask, cancellationToken.Token) 
     with ex -> 
      server.Stop() 

    member x.Stop() = 
     cancellationToken.Cancel() 
     server.Stop() 

    member x.ActiveConnections = 
     activeConnections 

И я делаю очень простой тест, который запускает сервер и убеждается исключение не выбрасывается, как следующим образом:

let cleanupTest (serverUsed:TCPListenerServer) = 
    serverUsed.Stop() 

[<TestMethod>] 
[<TestCategory(Networking)>] 
member x.``Start Server``() = 
    let server = new TCPListenerServer(44000) 
    server.Start() 
    Async.Sleep 5000 |> Async.RunSynchronously 

    cleanupTest server 

тест проходит, но я получаю следующее сообщение об ошибке в тестовом выходе в конце его:

System.AppDomainUnloadedException: пытающемуся получить доступ к выгружается AppDomain. Это может произойти, если тест (ы) начал поток, но не остановить его. Убедитесь, что все потоки, запущенные с помощью теста (ов), остановлены до завершения.

Оглядываясь назад на мой код, я понял, что моя рекурсивная петля никогда не заканчивается, поэтому я подумал, что это может быть причиной. Мое решение заключалось в том, чтобы добавить токен отмены (который находится в моем коде, который я вставил, его там не было), который я вызываю Cancel(), когда я останавливаю сервер.

К сожалению, это не решило проблему. Что может вызвать это исключение (которое не терпит неудачу в моем тестировании)?

ответ

0

Оказывается, мне нужно ждать, пока отмена не было фактически сделано. Я сделал это, спав 2 секунды после звонка до cancellationToken.Cancel(), так как у меня нет доступа к первоначальной задаче, созданной для выполнения следующих операций:

task.Wait()