<commit_before>﻿// Copyright (c) 2015 SIL International
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows.Forms;
using L10NSharp;
using Palaso.Code;
using Palaso.Reporting;
using ThreadState = System.Threading.ThreadState;


namespace Bloom.web
{
	public abstract class ServerBase : IDisposable
	{
		/// <summary>
		/// Prefix we add to http://localhost:8089 in all our urls. I'm not sure why...possibly to guard against
		/// some other program trying to use this port?
		/// </summary>
		internal const string BloomUrlPrefix = "/bloom/";

		/// <summary>
		/// Listens for requests on "http://localhost:8089/bloom/"
		/// </summary>
		private HttpListener _listener;

		/// <summary>
		/// Requests that come into the _listener are placed in the _queue so they can be processed
		/// </summary>
		private readonly Queue<HttpListenerContext> _queue;

		/// <summary>
		/// Gets requests from _listener and puts them in the _queue to be processed
		/// </summary>
		private readonly Thread _listenerThread;

		/// <summary>
		/// Pool of threads that pull a request from the _queue and processes it
		/// </summary>
		private readonly Thread[] _workers;

		/// <summary>
		/// Notifies threads that they should stop because the ServerBase object is being disposed
		/// </summary>
		private readonly ManualResetEvent _stop;

		/// <summary>
		/// Notifies threads in the _workers pool that there is a request in the _queue
		/// </summary>
		private readonly ManualResetEvent _ready;
		

		protected ServerBase()
		{
			// limit the number of worker threads to the number of processor cores
			_workers = new Thread[Environment.ProcessorCount];
			_queue = new Queue<HttpListenerContext>();
			_stop = new ManualResetEvent(false);
			_ready = new ManualResetEvent(false);
			_listenerThread = new Thread(HandleRequests);
		}

#if DEBUG
		/// <summary/>
		~ServerBase()
		{
			Dispose(false);
		}
#endif

		#region Startuup

		public void StartListening()
		{
			StartWithSetupIfNeeded();
		}

		protected virtual void StartWithSetupIfNeeded()
		{
			try
			{
				TryStart();
			}
			catch (Exception)
			{
				if (!Palaso.PlatformUtilities.Platform.IsWindows) throw;

				AddUrlAccessControlEntry();
				TryStart();
			}
		}

		private void TryStart()
		{
			_listener = new HttpListener {AuthenticationSchemes = AuthenticationSchemes.Anonymous};
			_listener.Prefixes.Add(PathEndingInSlash);
			_listener.Start();

			_listenerThread.Start();

			for (var i = 0; i < _workers.Length; i++)
			{
				_workers[i] = new Thread(RequestProcessorLoop);
				_workers[i].Start();
			}

			GetIsAbleToUsePort();
		}

		#endregion

		/// <summary>
		/// The _listenerThread runs this method, and exits when the _stop event is raised
		/// </summary>
		private void HandleRequests()
		{
			while (_listener.IsListening)
			{
				var context = _listener.BeginGetContext(QueueRequest, null);

				if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
					return;
			}
		}

		/// <summary>
		/// This method is called in the _listenerThread when we obtain an HTTP request from
		/// the _listener, and queues it for processing by a worker.
		/// </summary>
		/// <param name="ar"></param>
		private void QueueRequest(IAsyncResult ar)
		{
			// this can happen when shutting down
			if (!_listenerThread.IsAlive) return;

			lock (_queue)
			{
				_queue.Enqueue(_listener.EndGetContext(ar));
				_ready.Set();
			}
		}

		/// <summary>
		/// The worker threads run this function
		/// </summary>
		private void RequestProcessorLoop()
		{
			// _ready: indicates that there are requests in the queue that should be processed.
			// _stop:  indicates that the class is being disposed and the thread should terminate.
			WaitHandle[] wait = { _ready, _stop };

			// Wait until a request is ready or the thread is being stopped. The WaitAny will return 0 (the index of
			// _ready in the wait array) if a request is ready, and 1 when _stop is signaled, breaking us out of the loop.
			while (WaitHandle.WaitAny(wait) == 0)
			{
				HttpListenerContext context;
				lock (_queue)
				{
					if (_queue.Count > 0)
					{
						context = _queue.Dequeue();<commit_msg>BL-2207, check for listener closed before using it<commit_after>﻿// Copyright (c) 2015 SIL International
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows.Forms;
using L10NSharp;
using Palaso.Code;
using Palaso.Reporting;
using ThreadState = System.Threading.ThreadState;


namespace Bloom.web
{
	public abstract class ServerBase : IDisposable
	{
		/// <summary>
		/// Prefix we add to http://localhost:8089 in all our urls. I'm not sure why...possibly to guard against
		/// some other program trying to use this port?
		/// </summary>
		internal const string BloomUrlPrefix = "/bloom/";

		/// <summary>
		/// Listens for requests on "http://localhost:8089/bloom/"
		/// </summary>
		private HttpListener _listener;

		/// <summary>
		/// Requests that come into the _listener are placed in the _queue so they can be processed
		/// </summary>
		private readonly Queue<HttpListenerContext> _queue;

		/// <summary>
		/// Gets requests from _listener and puts them in the _queue to be processed
		/// </summary>
		private readonly Thread _listenerThread;

		/// <summary>
		/// Pool of threads that pull a request from the _queue and processes it
		/// </summary>
		private readonly Thread[] _workers;

		/// <summary>
		/// Notifies threads that they should stop because the ServerBase object is being disposed
		/// </summary>
		private readonly ManualResetEvent _stop;

		/// <summary>
		/// Notifies threads in the _workers pool that there is a request in the _queue
		/// </summary>
		private readonly ManualResetEvent _ready;
		

		protected ServerBase()
		{
			// limit the number of worker threads to the number of processor cores
			_workers = new Thread[Environment.ProcessorCount];
			_queue = new Queue<HttpListenerContext>();
			_stop = new ManualResetEvent(false);
			_ready = new ManualResetEvent(false);
			_listenerThread = new Thread(HandleRequests);
		}

#if DEBUG
		/// <summary/>
		~ServerBase()
		{
			Dispose(false);
		}
#endif

		#region Startuup

		public void StartListening()
		{
			StartWithSetupIfNeeded();
		}

		protected virtual void StartWithSetupIfNeeded()
		{
			try
			{
				TryStart();
			}
			catch (Exception)
			{
				if (!Palaso.PlatformUtilities.Platform.IsWindows) throw;

				AddUrlAccessControlEntry();
				TryStart();
			}
		}

		private void TryStart()
		{
			_listener = new HttpListener {AuthenticationSchemes = AuthenticationSchemes.Anonymous};
			_listener.Prefixes.Add(PathEndingInSlash);
			_listener.Start();

			_listenerThread.Start();

			for (var i = 0; i < _workers.Length; i++)
			{
				_workers[i] = new Thread(RequestProcessorLoop);
				_workers[i].Start();
			}

			GetIsAbleToUsePort();
		}

		#endregion

		/// <summary>
		/// The _listenerThread runs this method, and exits when the _stop event is raised
		/// </summary>
		private void HandleRequests()
		{
			while (_listener.IsListening)
			{
				var context = _listener.BeginGetContext(QueueRequest, null);

				if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
					return;
			}
		}

		/// <summary>
		/// This method is called in the _listenerThread when we obtain an HTTP request from
		/// the _listener, and queues it for processing by a worker.
		/// </summary>
		/// <param name="ar"></param>
		private void QueueRequest(IAsyncResult ar)
		{
			// this can happen when shutting down
			// BL-2207 indicates it may be possible for the thread to be alive and the listener closed,
			// although the only way I know it gets closed happens after joining with that thread.
			// Still, one more check seems worthwhile...if we're far enough along in shutting down
			// to have closed the listener we certainly can't respond to any more requests.
			if (!_listenerThread.IsAlive || !_listener.IsListening)
				return;

			lock (_queue)
			{
				_queue.Enqueue(_listener.EndGetContext(ar));
				_ready.Set();
			}
		}

		/// <summary>
		/// The worker threads run this function
		/// </summary>
		private void RequestProcessorLoop()
		{
			// _ready: indicates that there are requests in the queue that should be processed.
			// _stop:  indicates that the class is being disposed and the thread should terminate.
			WaitHandle[] wait = { _ready, _stop };

			// Wait until a request is ready or the thread is being stopped. The WaitAny will return 0 (the index of
			// _ready in the wait array) if a request is ready, and 1 when _stop is signaled, breaking us out of the loop.
			while (WaitHandle.WaitAny(wait) == 0)
			{
				HttpListenerContext context;
				lock (_queue)
				{
					if (_queue.Count > 0)
					{
						context = _queue.Dequeue();
{'fraction_non_alphanumeric': 0.1063296655409086, 'fraction_numerical': 0.005229327813487308, 'mean_word_length': 3.1184387617765816, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2244423', 'n_tokens_mistral': 3145, 'n_tokens_neox': 2637, 'n_words': 934}