2015-12-09 09:57:45 -08:00
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using System.Threading ;
using Microsoft.DotNet.ProjectModel.Server.Helpers ;
using Microsoft.DotNet.ProjectModel.Server.Messengers ;
using Microsoft.DotNet.ProjectModel.Server.Models ;
using Microsoft.Extensions.Logging ;
using NuGet.Frameworks ;
namespace Microsoft.DotNet.ProjectModel.Server
{
2016-01-02 00:44:59 -08:00
internal class ProjectManager
2015-12-09 09:57:45 -08:00
{
private readonly ILogger _log ;
private readonly object _processingLock = new object ( ) ;
private readonly Queue < Message > _inbox = new Queue < Message > ( ) ;
private readonly ProtocolManager _protocolManager ;
private ConnectionContext _initializedContext ;
// triggers
private readonly Trigger < string > _appPath = new Trigger < string > ( ) ;
private readonly Trigger < string > _configure = new Trigger < string > ( ) ;
private readonly Trigger < int > _refreshDependencies = new Trigger < int > ( ) ;
private readonly Trigger < int > _filesChanged = new Trigger < int > ( ) ;
2016-01-02 00:44:59 -08:00
private ProjectSnapshot _local = new ProjectSnapshot ( ) ;
private ProjectSnapshot _remote = new ProjectSnapshot ( ) ;
2015-12-09 09:57:45 -08:00
private readonly WorkspaceContext _workspaceContext ;
private int? _contextProtocolVersion ;
2016-01-02 00:44:59 -08:00
private readonly List < Messenger < ProjectContextSnapshot > > _messengers ;
2015-12-09 09:57:45 -08:00
private ProjectDiagnosticsMessenger _projectDiagnosticsMessenger ;
private GlobalErrorMessenger _globalErrorMessenger ;
private ProjectInformationMessenger _projectInforamtionMessenger ;
2016-01-02 00:44:59 -08:00
public ProjectManager ( int contextId ,
2015-12-09 09:57:45 -08:00
ILoggerFactory loggerFactory ,
WorkspaceContext workspaceContext ,
ProtocolManager protocolManager )
{
Id = contextId ;
2016-01-02 00:44:59 -08:00
_log = loggerFactory . CreateLogger < ProjectManager > ( ) ;
2015-12-09 09:57:45 -08:00
_workspaceContext = workspaceContext ;
_protocolManager = protocolManager ;
2016-01-02 00:44:59 -08:00
_messengers = new List < Messenger < ProjectContextSnapshot > >
2015-12-09 09:57:45 -08:00
{
new DependencyDiagnosticsMessenger ( Transmit ) ,
2016-01-14 16:24:59 -08:00
new ReferencesMessenger ( Transmit ) ,
2015-12-09 09:57:45 -08:00
new DependenciesMessenger ( Transmit ) ,
new CompilerOptionsMessenger ( Transmit ) ,
new SourcesMessenger ( Transmit )
} ;
_projectDiagnosticsMessenger = new ProjectDiagnosticsMessenger ( Transmit ) ;
2016-01-02 00:44:59 -08:00
_globalErrorMessenger = new GlobalErrorMessenger ( Transmit ) ;
2015-12-09 09:57:45 -08:00
_projectInforamtionMessenger = new ProjectInformationMessenger ( Transmit ) ;
}
public int Id { get ; }
public string ProjectPath { get { return _appPath . Value ; } }
public int ProtocolVersion
{
get
{
if ( _contextProtocolVersion . HasValue )
{
return _contextProtocolVersion . Value ;
}
else
{
return _protocolManager . CurrentVersion ;
}
}
}
public void OnReceive ( Message message )
{
lock ( _inbox )
{
_inbox . Enqueue ( message ) ;
}
2016-01-02 00:44:59 -08:00
ThreadPool . QueueUserWorkItem ( state = > ( ( ProjectManager ) state ) . ProcessLoop ( ) , this ) ;
2015-12-09 09:57:45 -08:00
}
private void Transmit ( string messageType , object payload )
{
var message = Message . FromPayload ( messageType , Id , payload ) ;
_initializedContext . Transmit ( message ) ;
}
private void ProcessLoop ( )
{
if ( ! Monitor . TryEnter ( _processingLock ) )
{
return ;
}
try
{
lock ( _inbox )
{
if ( ! _inbox . Any ( ) )
{
return ;
}
}
DoProcessLoop ( ) ;
}
catch ( Exception ex )
{
2016-02-29 19:55:01 -08:00
_log . LogError ( "Error occurs: {0}" , ex . ToString ( ) ) ;
2015-12-09 09:57:45 -08:00
var error = new ErrorMessage
{
Message = ex . Message
} ;
var fileFormatException = ex as FileFormatException ;
if ( fileFormatException ! = null )
{
error . Path = fileFormatException . Path ;
error . Line = fileFormatException . Line ;
error . Column = fileFormatException . Column ;
}
var message = Message . FromPayload ( MessageTypes . Error , Id , error ) ;
_initializedContext . Transmit ( message ) ;
_remote . GlobalErrorMessage = error ;
}
2016-01-13 22:14:04 -08:00
finally
{
Monitor . Exit ( _processingLock ) ;
}
2015-12-09 09:57:45 -08:00
}
private void DoProcessLoop ( )
{
while ( true )
{
DrainInbox ( ) ;
2016-01-13 22:14:04 -08:00
2016-01-02 00:44:59 -08:00
UpdateProject ( ) ;
SendOutgingMessages ( ) ;
2015-12-09 09:57:45 -08:00
lock ( _inbox )
{
if ( _inbox . Count = = 0 )
{
return ;
}
}
}
}
private void DrainInbox ( )
{
_log . LogInformation ( "Begin draining inbox." ) ;
while ( ProcessMessage ( ) ) { }
_log . LogInformation ( "Finish draining inbox." ) ;
}
private bool ProcessMessage ( )
{
Message message ;
lock ( _inbox )
{
if ( ! _inbox . Any ( ) )
{
return false ;
}
message = _inbox . Dequeue ( ) ;
Debug . Assert ( message ! = null ) ;
}
_log . LogInformation ( $"Received {message.MessageType}" ) ;
switch ( message . MessageType )
{
case MessageTypes . Initialize :
Initialize ( message ) ;
break ;
case MessageTypes . ChangeConfiguration :
// TODO: what if the payload is null or represent empty string?
_configure . Value = message . Payload . GetValue ( "Configuration" ) ;
break ;
case MessageTypes . RefreshDependencies :
case MessageTypes . RestoreComplete :
_refreshDependencies . Value = 0 ;
break ;
case MessageTypes . FilesChanged :
_filesChanged . Value = 0 ;
break ;
}
return true ;
}
private void Initialize ( Message message )
{
if ( _initializedContext ! = null )
{
_log . LogWarning ( $"Received {message.MessageType} message more than once for {_appPath.Value}" ) ;
return ;
}
_initializedContext = message . Sender ;
_appPath . Value = message . Payload . GetValue ( "ProjectFolder" ) ;
_configure . Value = message . Payload . GetValue ( "Configuration" ) ? ? "Debug" ;
var version = message . Payload . GetValue < int > ( "Version" ) ;
if ( version ! = 0 & & ! _protocolManager . EnvironmentOverridden )
{
_contextProtocolVersion = Math . Min ( version , _protocolManager . MaxVersion ) ;
_log . LogInformation ( $"Set context protocol version to {_contextProtocolVersion.Value}" ) ;
}
}
2016-01-02 00:44:59 -08:00
private bool UpdateProject ( )
2015-12-09 09:57:45 -08:00
{
2016-01-02 00:44:59 -08:00
ProjectSnapshot newSnapshot = null ;
2015-12-09 09:57:45 -08:00
if ( _appPath . WasAssigned | | _configure . WasAssigned | | _filesChanged . WasAssigned | | _refreshDependencies . WasAssigned )
{
_appPath . ClearAssigned ( ) ;
_configure . ClearAssigned ( ) ;
_filesChanged . ClearAssigned ( ) ;
_refreshDependencies . ClearAssigned ( ) ;
2016-01-02 00:44:59 -08:00
newSnapshot = ProjectSnapshot . Create ( _appPath . Value , _configure . Value , _workspaceContext , _remote . ProjectSearchPaths ) ;
2015-12-09 09:57:45 -08:00
}
2016-01-02 00:44:59 -08:00
if ( newSnapshot = = null )
2015-12-09 09:57:45 -08:00
{
return false ;
}
2016-01-02 00:44:59 -08:00
_local = newSnapshot ;
2015-12-09 09:57:45 -08:00
return true ;
}
2016-01-02 00:44:59 -08:00
private void SendOutgingMessages ( )
2015-12-09 09:57:45 -08:00
{
_projectInforamtionMessenger . UpdateRemote ( _local , _remote ) ;
_projectDiagnosticsMessenger . UpdateRemote ( _local , _remote ) ;
2016-01-02 00:44:59 -08:00
var unprocessedFrameworks = new HashSet < NuGetFramework > ( _remote . ProjectContexts . Keys ) ;
foreach ( var pair in _local . ProjectContexts )
2015-12-09 09:57:45 -08:00
{
2016-01-02 00:44:59 -08:00
ProjectContextSnapshot localProjectSnapshot = pair . Value ;
ProjectContextSnapshot remoteProjectSnapshot ;
2015-12-09 09:57:45 -08:00
2016-01-02 00:44:59 -08:00
if ( ! _remote . ProjectContexts . TryGetValue ( pair . Key , out remoteProjectSnapshot ) )
2015-12-09 09:57:45 -08:00
{
2016-01-02 00:44:59 -08:00
remoteProjectSnapshot = new ProjectContextSnapshot ( ) ;
_remote . ProjectContexts [ pair . Key ] = remoteProjectSnapshot ;
2015-12-09 09:57:45 -08:00
}
unprocessedFrameworks . Remove ( pair . Key ) ;
2016-01-02 00:44:59 -08:00
foreach ( var messenger in _messengers )
2015-12-09 09:57:45 -08:00
{
2016-01-02 00:44:59 -08:00
messenger . UpdateRemote ( localProjectSnapshot ,
2015-12-09 09:57:45 -08:00
remoteProjectSnapshot ) ;
}
}
// Remove all processed frameworks from the remote view
foreach ( var framework in unprocessedFrameworks )
{
2016-01-02 00:44:59 -08:00
_remote . ProjectContexts . Remove ( framework ) ;
2015-12-09 09:57:45 -08:00
}
2016-01-13 22:14:04 -08:00
2015-12-09 09:57:45 -08:00
_globalErrorMessenger . UpdateRemote ( _local , _remote ) ;
}
private class Trigger < TValue >
{
private TValue _value ;
public bool WasAssigned { get ; private set ; }
public void ClearAssigned ( )
{
WasAssigned = false ;
}
public TValue Value
{
get { return _value ; }
set
{
WasAssigned = true ;
_value = value ;
}
}
}
}
}