Skip to content

Commit e88cefe

Browse files
committed
Make JsPool generic to allow for using other engines directly in the future
1 parent f47a3ca commit e88cefe

File tree

7 files changed

+131
-75
lines changed

7 files changed

+131
-75
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) 2016 Daniel Lo Nigro (Daniel15)
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
using JavaScriptEngineSwitcher.Core;
9+
10+
namespace JSPool
11+
{
12+
/// <summary>
13+
/// Handles acquiring JavaScript engines from a shared pool. This class is thread safe.
14+
/// </summary>
15+
public interface IJsPool : IJsPool<IJsEngine>
16+
{
17+
}
18+
}

src/JSPool/IJsPool.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
/*
2-
* Copyright (c) 2014 Daniel Lo Nigro (Daniel15)
2+
* Copyright (c) 2014-2016 Daniel Lo Nigro (Daniel15)
33
*
44
* This source code is licensed under the BSD-style license found in the
55
* LICENSE file in the root directory of this source tree.
66
*/
77

88
using System;
9-
using JavaScriptEngineSwitcher.Core;
109
using JSPool.Exceptions;
1110

1211
namespace JSPool
1312
{
1413
/// <summary>
1514
/// Handles acquiring JavaScript engines from a shared pool. This class is thread safe.
1615
/// </summary>
17-
public interface IJsPool : IDisposable
16+
public interface IJsPool<T> : IDisposable
1817
{
1918
/// <summary>
2019
/// Gets an engine from the pool. This engine should be returned to the pool via
@@ -32,13 +31,13 @@ public interface IJsPool : IDisposable
3231
/// <exception cref="JsPoolExhaustedException">
3332
/// Thrown if no engines are available in the pool within the provided timeout period.
3433
/// </exception>
35-
IJsEngine GetEngine(TimeSpan? timeout = null);
34+
T GetEngine(TimeSpan? timeout = null);
3635

3736
/// <summary>
3837
/// Returns an engine to the pool so it can be reused
3938
/// </summary>
4039
/// <param name="engine">Engine to return</param>
41-
void ReturnEngineToPool(IJsEngine engine);
40+
void ReturnEngineToPool(T engine);
4241

4342
/// <summary>
4443
/// Gets the total number of engines in this engine pool, including engines that are
@@ -58,7 +57,7 @@ public interface IJsPool : IDisposable
5857
/// <param name="repopulateEngines">
5958
/// If <c>true</c>, a new engine will be created to replace the disposed engine
6059
/// </param>
61-
void DisposeEngine(IJsEngine engine, bool repopulateEngines = true);
60+
void DisposeEngine(T engine, bool repopulateEngines = true);
6261

6362
/// <summary>
6463
/// Disposes all engines in this pool, and creates new engines in their place.

src/JSPool/JSPool.csproj

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@
5858
<Compile Include="FileWatcher.cs" />
5959
<Compile Include="IFileWatcher.cs" />
6060
<Compile Include="IJsEngineWithOwnThread.cs" />
61+
<Compile Include="IJavaScriptEngineSwitcherPool.cs" />
6162
<Compile Include="IJsPool.cs" />
62-
<Compile Include="JsEngineExtensions.cs" />
6363
<Compile Include="JsEngineWithOwnThread.cs" />
64-
<Compile Include="JsPool.cs" />
65-
<Compile Include="JsPoolConfig.cs" />
64+
<Compile Include="JavaScriptEngineSwitcherPool.cs" />
65+
<Compile Include="JavaScriptEngineSwitcherPoolConfig.cs" />
6666
<Compile Include="Exceptions\JsPoolExhaustedException.cs" />
67+
<Compile Include="JsPoolConfig.cs" />
68+
<Compile Include="JsPool.cs" />
6769
<Compile Include="Properties\AssemblyInfo.cs" />
6870
</ItemGroup>
6971
<ItemGroup>

src/JSPool/JsEngineExtensions.cs renamed to src/JSPool/JavaScriptEngineSwitcherPool.cs

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,65 +6,78 @@
66
*/
77

88
using System;
9+
using System.Diagnostics;
910
using System.Reflection;
1011
using JavaScriptEngineSwitcher.Core;
1112

1213
namespace JSPool
1314
{
1415
/// <summary>
15-
/// Extension methods for <see cref="IJsEngine"/>.
16+
/// Handles acquiring JavaScript engines from a shared pool. This class is thread-safe.
1617
/// </summary>
17-
public static class JsEngineExtensions
18+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
19+
public class JsPool : JsPool<IJsEngine>
1820
{
1921
private const string MSIE_TYPE = "MsieJsEngine";
2022
private const string V8_TYPE = "V8JsEngine";
2123

2224
/// <summary>
23-
/// Determines if the specified engine can only be used on the thread it is created on.
25+
/// Creates a new JavaScript engine pool
2426
/// </summary>
25-
/// <param name="engine">Engine</param>
26-
/// <returns><c>true</c> if the engine should be confined to a single thread</returns>
27-
public static bool NeedsOwnThread(this IJsEngine engine)
27+
/// <param name="config">
28+
/// The configuration to use. If not provided, a default configuration will be used.
29+
/// </param>
30+
public JsPool(JsPoolConfig config = null)
31+
: base(config ?? new JsPoolConfig())
2832
{
29-
// Checking MsieJsEngine as a string so we don't need to pull in an otherwise
30-
// unneeded dependency on MsieJsEngine.
31-
return engine.GetType().Name == MSIE_TYPE;
3233
}
3334

3435
/// <summary>
35-
/// Determines if the specified engine supports garbage collection.
36+
/// Gets a factory method used to create engines.
3637
/// </summary>
37-
/// <param name="engine">Engine</param>
38-
/// <returns><c>true</c> if the engine supports garbage collection</returns>
39-
public static bool SupportsGarbageCollection(this IJsEngine engine)
38+
protected override Func<IJsEngine> CreateEngineFactory()
4039
{
41-
return engine.GetType().Name == V8_TYPE;
40+
using (var tempEngine = _config.EngineFactory())
41+
{
42+
if (!NeedsOwnThread(tempEngine))
43+
{
44+
// Engine is fine with being accessed across multiple threads, we can just
45+
// return its factory directly.
46+
return _config.EngineFactory;
47+
}
48+
// Engine needs special treatment. This is the case with the MSIE engine, which
49+
// can only be accessed from the thread it was created on. In this case we need
50+
// to create the engine in a separate thread and marshall the requests across.
51+
return () => new JsEngineWithOwnThread(_config.EngineFactory, _cancellationTokenSource.Token);
52+
}
4253
}
4354

4455
/// <summary>
45-
/// Collecs garbage in the specified engine.
56+
/// Determines if the specified engine can only be used on the thread it is created on.
4657
/// </summary>
47-
/// <param name="engine">Engine to collect garbage in</param>
48-
public static void CollectGarbage(this IJsEngine engine)
58+
/// <param name="engine">Engine</param>
59+
/// <returns><c>true</c> if the engine should be confined to a single thread</returns>
60+
private static bool NeedsOwnThread(IJsEngine engine)
4961
{
50-
if (!engine.SupportsGarbageCollection())
51-
{
52-
throw new InvalidOperationException("Engine doesn't support garbage collection.");
53-
}
54-
55-
V8CollectGarbage(engine);
62+
// Checking MsieJsEngine as a string so we don't need to pull in an otherwise
63+
// unneeded dependency on MsieJsEngine.
64+
return engine.GetType().Name == MSIE_TYPE;
5665
}
5766

5867
#region V8 Garbage Collection implementation
5968
private static FieldInfo _innerEngineField;
6069
private static MethodInfo _collectGarbageMethod;
61-
6270
/// <summary>
63-
/// Collects garbage in the specified V8 engine.
71+
/// Runs garbage collection for the specified engine
6472
/// </summary>
6573
/// <param name="engine"></param>
66-
private static void V8CollectGarbage(IJsEngine engine)
74+
protected override void CollectGarbage(IJsEngine engine)
6775
{
76+
if (engine.GetType().Name != V8_TYPE)
77+
{
78+
return;
79+
}
80+
6881
// Since JavaScriptEngineSwitcher does not expose the inner JavaScript engine, we need
6982
// to use reflection to get to it.
7083
if (_innerEngineField == null)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2014-2016 Daniel Lo Nigro (Daniel15)
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
using JavaScriptEngineSwitcher.Core;
9+
10+
namespace JSPool
11+
{
12+
/// <summary>
13+
/// Contains the configuration information for JSPool
14+
/// </summary>
15+
public class JsPoolConfig : JsPoolConfig<IJsEngine>
16+
{
17+
/// <summary>
18+
/// Creates a new JavaScript pool configuration. Default values will be set automatically.
19+
/// </summary>
20+
public JsPoolConfig()
21+
{
22+
EngineFactory = JsEngineSwitcher.Current.CreateDefaultJsEngineInstance;
23+
}
24+
}
25+
}

src/JSPool/JsPool.cs

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Collections.Generic;
1111
using System.Diagnostics;
1212
using System.Threading;
13-
using JavaScriptEngineSwitcher.Core;
1413
using JSPool.Exceptions;
1514

1615
namespace JSPool
@@ -19,24 +18,24 @@ namespace JSPool
1918
/// Handles acquiring JavaScript engines from a shared pool. This class is thread-safe.
2019
/// </summary>
2120
[DebuggerDisplay("{DebuggerDisplay,nq}")]
22-
public class JsPool : IJsPool
21+
public class JsPool<T> : IJsPool<T>
2322
{
2423
/// <summary>
2524
/// Configuration for this engine pool.
2625
/// </summary>
27-
protected readonly JsPoolConfig _config;
26+
protected readonly JsPoolConfig<T> _config;
2827
/// <summary>
2928
/// Engines that are currently available for use.
3029
/// </summary>
31-
protected readonly BlockingCollection<IJsEngine> _availableEngines = new BlockingCollection<IJsEngine>();
30+
protected readonly BlockingCollection<T> _availableEngines = new BlockingCollection<T>();
3231
/// <summary>
3332
/// Metadata for the engines. Total number of engines that have been created is reflected in its Count.
3433
/// </summary>
35-
protected readonly IDictionary<IJsEngine, EngineMetadata> _metadata = new ConcurrentDictionary<IJsEngine, EngineMetadata>();
34+
protected readonly IDictionary<T, EngineMetadata> _metadata = new ConcurrentDictionary<T, EngineMetadata>();
3635
/// <summary>
3736
/// Factory method used to create engines.
3837
/// </summary>
39-
protected readonly Func<IJsEngine> _engineFactory;
38+
protected readonly Func<T> _engineFactory;
4039
/// <summary>
4140
/// Handles watching for changes to files, to recycle the engines if any related files change.
4241
/// </summary>
@@ -62,9 +61,9 @@ public class JsPool : IJsPool
6261
/// <param name="config">
6362
/// The configuration to use. If not provided, a default configuration will be used.
6463
/// </param>
65-
public JsPool(JsPoolConfig config = null)
64+
public JsPool(JsPoolConfig<T> config)
6665
{
67-
_config = config ?? new JsPoolConfig();
66+
_config = config;
6867
_engineFactory = CreateEngineFactory();
6968
PopulateEngines();
7069
InitializeWatcher();
@@ -73,21 +72,9 @@ public JsPool(JsPoolConfig config = null)
7372
/// <summary>
7473
/// Gets a factory method used to create engines.
7574
/// </summary>
76-
protected virtual Func<IJsEngine> CreateEngineFactory()
75+
protected virtual Func<T> CreateEngineFactory()
7776
{
78-
using (var tempEngine = _config.EngineFactory())
79-
{
80-
if (!tempEngine.NeedsOwnThread())
81-
{
82-
// Engine is fine with being accessed across multiple threads, we can just
83-
// return its factory directly.
84-
return _config.EngineFactory;
85-
}
86-
// Engine needs special treatment. This is the case with the MSIE engine, which
87-
// can only be accessed from the thread it was created on. In this case we need
88-
// to create the engine in a separate thread and marshall the requests across.
89-
return () => new JsEngineWithOwnThread(_config.EngineFactory, _cancellationTokenSource.Token);
90-
}
77+
return _config.EngineFactory;
9178
}
9279

9380
/// <summary>
@@ -122,7 +109,7 @@ protected virtual void PopulateEngines()
122109
/// <summary>
123110
/// Creates a new JavaScript engine and adds it to the list of all available engines.
124111
/// </summary>
125-
protected virtual IJsEngine CreateEngine()
112+
protected virtual T CreateEngine()
126113
{
127114
var engine = _engineFactory();
128115
_config.Initializer(engine);
@@ -146,9 +133,9 @@ protected virtual IJsEngine CreateEngine()
146133
/// <exception cref="JsPoolExhaustedException">
147134
/// Thrown if no engines are available in the pool within the provided timeout period.
148135
/// </exception>
149-
public virtual IJsEngine GetEngine(TimeSpan? timeout = null)
136+
public virtual T GetEngine(TimeSpan? timeout = null)
150137
{
151-
IJsEngine engine;
138+
T engine;
152139

153140
// First see if a pooled engine is immediately available
154141
if (_availableEngines.TryTake(out engine))
@@ -183,7 +170,7 @@ public virtual IJsEngine GetEngine(TimeSpan? timeout = null)
183170
/// Marks the specified engine as "in use"
184171
/// </summary>
185172
/// <param name="engine"></param>
186-
private IJsEngine TakeEngine(IJsEngine engine)
173+
private T TakeEngine(T engine)
187174
{
188175
var metadata = _metadata[engine];
189176
metadata.InUse = true;
@@ -195,15 +182,18 @@ private IJsEngine TakeEngine(IJsEngine engine)
195182
/// Returns an engine to the pool so it can be reused
196183
/// </summary>
197184
/// <param name="engine">Engine to return</param>
198-
public virtual void ReturnEngineToPool(IJsEngine engine)
185+
public virtual void ReturnEngineToPool(T engine)
199186
{
200187
EngineMetadata metadata;
201188
if (!_metadata.TryGetValue(engine, out metadata))
202189
{
203190
// This engine was from another pool. This could happen if a pool is recycled
204191
// and replaced with a different one (like what ReactJS.NET does when any
205192
// loaded files change). Let's just pretend we never saw it.
206-
engine.Dispose();
193+
if (engine is IDisposable)
194+
{
195+
((IDisposable)engine).Dispose();
196+
}
207197
return;
208198
}
209199

@@ -218,11 +208,10 @@ public virtual void ReturnEngineToPool(IJsEngine engine)
218208

219209
if (
220210
_config.GarbageCollectionInterval > 0 &&
221-
usageCount % _config.GarbageCollectionInterval == 0 &&
222-
engine.SupportsGarbageCollection()
211+
metadata.UsageCount % _config.GarbageCollectionInterval == 0
223212
)
224213
{
225-
engine.CollectGarbage();
214+
CollectGarbage(engine);
226215
}
227216

228217
_availableEngines.Add(engine);
@@ -235,9 +224,12 @@ public virtual void ReturnEngineToPool(IJsEngine engine)
235224
/// <param name="repopulateEngines">
236225
/// If <c>true</c>, a new engine will be created to replace the disposed engine
237226
/// </param>
238-
public virtual void DisposeEngine(IJsEngine engine, bool repopulateEngines = true)
227+
public virtual void DisposeEngine(T engine, bool repopulateEngines = true)
239228
{
240-
engine.Dispose();
229+
if (engine is IDisposable)
230+
{
231+
((IDisposable)engine).Dispose();
232+
}
241233
_metadata.Remove(engine);
242234

243235
if (repopulateEngines)
@@ -254,7 +246,7 @@ public virtual void DisposeEngine(IJsEngine engine, bool repopulateEngines = tru
254246
/// </summary>
255247
protected virtual void DisposeAllEngines()
256248
{
257-
IJsEngine engine;
249+
T engine;
258250
while (_availableEngines.TryTake(out engine))
259251
{
260252
DisposeEngine(engine, repopulateEngines: false);
@@ -290,6 +282,15 @@ public virtual void Dispose()
290282
_fileWatcher.Dispose();
291283
}
292284
}
285+
286+
/// <summary>
287+
/// Runs garbage collection for the specified engine
288+
/// </summary>
289+
/// <param name="engine"></param>
290+
protected virtual void CollectGarbage(T engine)
291+
{
292+
// No-op by default
293+
}
293294

294295
#region Statistics and debugging
295296
/// <summary>

0 commit comments

Comments
 (0)