Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
olivier-spinelli committed Aug 21, 2017
2 parents dbf9612 + ad160fc commit 5655103
Show file tree
Hide file tree
Showing 15 changed files with 882 additions and 284 deletions.
10 changes: 8 additions & 2 deletions CK-Monitoring.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26403.7
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C51E8D11-FC45-4D5B-85DC-68A7EEB83601}"
ProjectSection(SolutionItems) = preProject
Expand All @@ -17,11 +17,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1E041F87
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CK.Monitoring", "CK.Monitoring\CK.Monitoring.csproj", "{4A4EAEFF-2A8B-4308-A178-3FFEE94CC009}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CK.Monitoring.Tests", "Tests\CK.Monitoring.Tests\CK.Monitoring.Tests.csproj", "{D9AC45A9-A7F9-4070-8AB4-50D76C688912}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CK.Monitoring.Tests", "Tests\CK.Monitoring.Tests\CK.Monitoring.Tests.csproj", "{D9AC45A9-A7F9-4070-8AB4-50D76C688912}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCakeBuilder", "CodeCakeBuilder\CodeCakeBuilder.csproj", "{FD4817B6-3CD7-4E74-AA10-7CA95FDFCF2D}"
EndProject
Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Expand All @@ -46,4 +49,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{D9AC45A9-A7F9-4070-8AB4-50D76C688912} = {1E041F87-872B-43D0-B107-2D998F7AEE38}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0AA77C05-08ED-458A-A69D-43B7D4CD5324}
EndGlobalSection
EndGlobal
154 changes: 110 additions & 44 deletions CK.Monitoring/DispatcherSink.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using CK.Core;
using CK.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
Expand All @@ -17,6 +17,8 @@ internal class DispatcherSink : IGrandOutputSink
readonly List<IGrandOutputHandler> _handlers;
readonly long _deltaExternalTicks;
readonly Action _externalOnTimer;
readonly object _confTrigger;
readonly Action<IActivityMonitor> _initialRegister;

GrandOutputConfiguration[] _newConf;
TimeSpan _timerDuration;
Expand All @@ -26,11 +28,13 @@ internal class DispatcherSink : IGrandOutputSink
volatile int _stopFlag;
volatile bool _forceClose;

public DispatcherSink( TimeSpan timerDuration, TimeSpan externalTimerDuration, Action externalTimer )
public DispatcherSink( Action<IActivityMonitor> initialRegister, TimeSpan timerDuration, TimeSpan externalTimerDuration, Action externalTimer )
{
_initialRegister = initialRegister;
_queue = new BlockingCollection<GrandOutputEventInfo>();
_handlers = new List<IGrandOutputHandler>();
_task = new Task( Process, TaskCreationOptions.LongRunning );
_confTrigger = new object();
_timerDuration = timerDuration;
_deltaTicks = timerDuration.Ticks;
_deltaExternalTicks = externalTimerDuration.Ticks;
Expand All @@ -56,51 +60,26 @@ public TimeSpan TimerDuration

void Process()
{
int nbEvent = 0;
IActivityMonitor monitor = new SystemActivityMonitor( applyAutoConfigurations: false, topic: GetType().FullName );
var monitor = new ActivityMonitor( applyAutoConfigurations: false );
// Simple pooling for initial configuration.
GrandOutputConfiguration[] newConf = _newConf;
while( newConf == null )
{
Thread.Sleep( 0 );
newConf = _newConf;
}
_initialRegister( monitor );
monitor.SetTopic( GetType().FullName );
DoConfigure( monitor, newConf );
while( !_queue.IsCompleted && !_forceClose )
{
bool hasEvent = _queue.TryTake( out GrandOutputEventInfo e, millisecondsTimeout: 100 );
var newConf = _newConf;
if( newConf != null && newConf.Length > 0 )
{
Util.InterlockedSet( ref _newConf, t => t.Skip( newConf.Length ).ToArray() );
var c = newConf[newConf.Length - 1];
TimerDuration = c.TimerDuration;
List<IGrandOutputHandler> toKeep = new List<IGrandOutputHandler>();
for( int iConf = 0; iConf < c.Handlers.Count; ++iConf )
{
for( int iHandler = 0; iHandler < _handlers.Count; ++iHandler )
{
if( _handlers[iHandler].ApplyConfiguration( monitor, c.Handlers[iConf] ) )
{
c.Handlers.RemoveAt( iConf-- );
toKeep.Add( _handlers[iHandler] );
_handlers.RemoveAt( iHandler );
break;
}
}
}
foreach( var h in _handlers )
{
SafeActivateOrDeactivate( monitor, h, false );
}
_handlers.Clear();
_handlers.AddRange( toKeep );
foreach( var conf in c.Handlers )
{
var h = GrandOutput.CreateHandler( conf );
if( SafeActivateOrDeactivate( monitor, h, true ) )
{
_handlers.Add( h );
}
}
}
newConf = _newConf;
if( newConf.Length > 0 ) DoConfigure( monitor, newConf );
List<IGrandOutputHandler> faulty = null;
#region Process event if any.
if( hasEvent )
{
++nbEvent;
foreach( var h in _handlers )
{
try
Expand All @@ -109,7 +88,9 @@ void Process()
}
catch( Exception ex )
{
monitor.SendLine( LogLevel.Error, h.GetType().FullName + ".Handle", ex );
var msg = $"{h.GetType().FullName}.Handle() crashed.";
ActivityMonitor.CriticalErrorCollector.Add( ex, msg );
monitor.SendLine( LogLevel.Fatal, msg, ex );
if( faulty == null ) faulty = new List<IGrandOutputHandler>();
faulty.Add( h );
}
Expand All @@ -128,7 +109,9 @@ void Process()
}
catch( Exception ex )
{
monitor.SendLine( LogLevel.Error, h.GetType().FullName + ".OnTimer", ex );
var msg = $"{h.GetType().FullName}.OnTimer() crashed.";
ActivityMonitor.CriticalErrorCollector.Add( ex, msg );
monitor.SendLine( LogLevel.Fatal, msg, ex );
if( faulty == null ) faulty = new List<IGrandOutputHandler>();
faulty.Add( h );
}
Expand All @@ -155,6 +138,71 @@ void Process()
monitor.MonitorEnd();
}

private void DoConfigure( IActivityMonitor monitor, GrandOutputConfiguration[] newConf )
{
Util.InterlockedSet( ref _newConf, t => t.Skip( newConf.Length ).ToArray() );
var c = newConf[newConf.Length - 1];
TimerDuration = c.TimerDuration;
List<IGrandOutputHandler> toKeep = new List<IGrandOutputHandler>();
for( int iConf = 0; iConf < c.Handlers.Count; ++iConf )
{
for( int iHandler = 0; iHandler < _handlers.Count; ++iHandler )
{
try
{
if( _handlers[iHandler].ApplyConfiguration( monitor, c.Handlers[iConf] ) )
{
// Existing _handlers[iHandler] accepted the new c.Handlers[iConf].
c.Handlers.RemoveAt( iConf-- );
toKeep.Add( _handlers[iHandler] );
_handlers.RemoveAt( iHandler );
break;
}
}
catch( Exception ex )
{
var h = _handlers[iHandler];
// Existing _handlers[iHandler] crashed with the proposed c.Handlers[iConf].
var msg = $"Existing {h.GetType().FullName} crashed with the configuration {c.Handlers[iConf].GetType().FullName}.";
ActivityMonitor.CriticalErrorCollector.Add( ex, msg );
monitor.SendLine( LogLevel.Fatal, msg, ex );
// Since the handler can be compromised, we skip it from any subsequent
// attempt to reconfigure it and deactivate it.
_handlers.RemoveAt( iHandler-- );
SafeActivateOrDeactivate( monitor, h, false );
}
}
}
// Deactivate and get rid of remaining handlers.
foreach( var h in _handlers )
{
SafeActivateOrDeactivate( monitor, h, false );
}
_handlers.Clear();
// Restores reconfigured handlers.
_handlers.AddRange( toKeep );
// Creates and activates new handlers.
foreach( var conf in c.Handlers )
{
try
{
var h = GrandOutput.CreateHandler( conf );
if( SafeActivateOrDeactivate( monitor, h, true ) )
{
_handlers.Add( h );
}
}
catch( Exception ex )
{
var msg = $"While creating handler for {conf.GetType().FullName}.";
ActivityMonitor.CriticalErrorCollector.Add( ex, msg );
monitor.SendLine( LogLevel.Fatal, msg, ex );
}
}
lock( _confTrigger )
Monitor.PulseAll( _confTrigger );
}

bool SafeActivateOrDeactivate( IActivityMonitor monitor, IGrandOutputHandler h, bool activate )
{
try
Expand All @@ -164,12 +212,21 @@ bool SafeActivateOrDeactivate( IActivityMonitor monitor, IGrandOutputHandler h,
}
catch( Exception ex )
{
monitor.SendLine( LogLevel.Error, h.GetType().FullName, ex );
var msg = $"Handler {h.GetType().FullName} crashed during {(activate?"activation":"de-activation")}.";
ActivityMonitor.CriticalErrorCollector.Add( ex, msg );
monitor.SendLine( LogLevel.Fatal, msg, ex );
return false;
}
return true;
}

/// <summary>
/// Starts stopping this sink, returning true iif this call
/// actually stopped it.
/// </summary>
/// <returns>
/// True if this call stopped this sink, false if it has been already been stopped by another thread.
/// </returns>
public bool Stop()
{
if( Interlocked.Exchange( ref _stopFlag, 1 ) == 0 )
Expand All @@ -191,10 +248,19 @@ public void Finalize( int millisecondsBeforeForceClose )

public void Handle( GrandOutputEventInfo logEvent ) => _queue.Add( logEvent );

public void ApplyConfiguration( GrandOutputConfiguration configuration )
public void ApplyConfiguration( GrandOutputConfiguration configuration, bool waitForApplication )
{
Debug.Assert( configuration.InternalClone );
Util.InterlockedAdd( ref _newConf, configuration );
if( waitForApplication )
{
lock( _confTrigger )
{
GrandOutputConfiguration[] newConf;
while( _stopFlag == 0 && (newConf = _newConf) != null && newConf.Contains( configuration ) )
Monitor.Wait( _confTrigger );
}
}
}
}
}
Loading

0 comments on commit 5655103

Please sign in to comment.