-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
CompositionOperation.cs
132 lines (112 loc) · 4.35 KB
/
CompositionOperation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace System.Composition.Hosting.Core
{
/// <summary>
/// Represents a single logical graph-building operation.
/// </summary>
/// <remarks>Instances of this class are not safe for access by multiple threads.</remarks>
public sealed class CompositionOperation : IDisposable
{
private List<Action> _nonPrerequisiteActions;
private List<Action> _postCompositionActions;
private object _sharingLock;
// Construct using Run() method.
private CompositionOperation() { }
/// <summary>
/// Execute a new composition operation starting within the specified lifetime
/// context, for the specified activator.
/// </summary>
/// <param name="outermostLifetimeContext">Context in which to begin the operation (the operation can flow
/// to the parents of the context if required).</param>
/// <param name="compositionRootActivator">Activator that will drive the operation.</param>
/// <returns>The composed object graph.</returns>
public static object Run(LifetimeContext outermostLifetimeContext, CompositeActivator compositionRootActivator)
{
if (outermostLifetimeContext is null)
{
throw new ArgumentNullException(nameof(outermostLifetimeContext));
}
if (compositionRootActivator is null)
{
throw new ArgumentNullException(nameof(compositionRootActivator));
}
using (var operation = new CompositionOperation())
{
var result = compositionRootActivator(outermostLifetimeContext, operation);
operation.Complete();
return result;
}
}
/// <summary>
/// Called during the activation process to specify an action that can run after all
/// prerequisite part dependencies have been satisfied.
/// </summary>
/// <param name="action">Action to run.</param>
public void AddNonPrerequisiteAction(Action action)
{
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
_nonPrerequisiteActions ??= new List<Action>();
_nonPrerequisiteActions.Add(action);
}
/// <summary>
/// Called during the activation process to specify an action that must run only after
/// all composition has completed. See OnImportsSatisfiedAttribute.
/// </summary>
/// <param name="action">Action to run.</param>
public void AddPostCompositionAction(Action action)
{
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
_postCompositionActions ??= new List<Action>();
_postCompositionActions.Add(action);
}
internal void EnterSharingLock(object sharingLock)
{
Debug.Assert(sharingLock != null, "Expected a sharing lock to be passed.");
if (_sharingLock == null)
{
_sharingLock = sharingLock;
Monitor.Enter(sharingLock);
}
if (_sharingLock != sharingLock)
{
throw new Exception(SR.Sharing_Lock_Taken);
}
}
private void Complete()
{
while (_nonPrerequisiteActions != null)
RunAndClearActions();
if (_postCompositionActions != null)
{
foreach (var action in _postCompositionActions)
action();
_postCompositionActions = null;
}
}
private void RunAndClearActions()
{
var currentActions = _nonPrerequisiteActions;
_nonPrerequisiteActions = null;
foreach (var action in currentActions)
action();
}
/// <summary>
/// Release locks held during the operation.
/// </summary>
public void Dispose()
{
if (_sharingLock != null)
Monitor.Exit(_sharingLock);
}
}
}