1
+ // Licensed to the .NET Foundation under one or more agreements.
2
+ // The .NET Foundation licenses this file to you under the MIT license.
3
+ // See the LICENSE file in the project root for more information.
4
+
5
+ // Ported from Roslyn, see: https://github.com/dotnet/roslyn/blob/main/src/Dependencies/PooledObjects/ObjectPool%601.cs.
6
+
7
+ using System ;
8
+ using System . Runtime . CompilerServices ;
9
+ using System . Threading ;
10
+
11
+ namespace Microsoft . CodeAnalysis . PooledObjects ;
12
+
13
+ /// <summary>
14
+ /// <para>
15
+ /// Generic implementation of object pooling pattern with predefined pool size limit. The main purpose
16
+ /// is that limited number of frequently used objects can be kept in the pool for further recycling.
17
+ /// </para>
18
+ /// <para>
19
+ /// Notes:
20
+ /// <list type="number">
21
+ /// <item>
22
+ /// It is not the goal to keep all returned objects. Pool is not meant for storage. If there
23
+ /// is no space in the pool, extra returned objects will be dropped.
24
+ /// </item>
25
+ /// <item>
26
+ /// It is implied that if object was obtained from a pool, the caller will return it back in
27
+ /// a relatively short time. Keeping checked out objects for long durations is ok, but
28
+ /// reduces usefulness of pooling. Just new up your own.
29
+ /// </item>
30
+ /// </list>
31
+ /// </para>
32
+ /// <para>
33
+ /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice.
34
+ /// Rationale: if there is no intent for reusing the object, do not use pool - just use "new".
35
+ /// </para>
36
+ /// </summary>
37
+ /// <typeparam name="T">The type of objects to pool.</typeparam>
38
+ internal sealed class ObjectPool < T >
39
+ where T : class
40
+ {
41
+ /// <summary>
42
+ /// The factory is stored for the lifetime of the pool. We will call this only when pool needs to
43
+ /// expand. compared to "new T()", Func gives more flexibility to implementers and faster than "new T()".
44
+ /// </summary>
45
+ private readonly Func < T > factory ;
46
+
47
+ /// <summary>
48
+ /// The array of cached items.
49
+ /// </summary>
50
+ private readonly Element [ ] items ;
51
+
52
+ /// <summary>
53
+ /// Storage for the pool objects. The first item is stored in a dedicated field
54
+ /// because we expect to be able to satisfy most requests from it.
55
+ /// </summary>
56
+ private T ? firstItem ;
57
+
58
+ /// <summary>
59
+ /// Creates a new <see cref="ObjectPool{T}"/> instance with the specified parameters.
60
+ /// </summary>
61
+ /// <param name="factory">The input factory to produce <typeparamref name="T"/> items.</param>
62
+ public ObjectPool ( Func < T > factory )
63
+ : this ( factory , Environment . ProcessorCount * 2 )
64
+ {
65
+ }
66
+
67
+ /// <summary>
68
+ /// Creates a new <see cref="ObjectPool{T}"/> instance with the specified parameters.
69
+ /// </summary>
70
+ /// <param name="factory">The input factory to produce <typeparamref name="T"/> items.</param>
71
+ /// <param name="size">The pool size to use.</param>
72
+ public ObjectPool ( Func < T > factory , int size )
73
+ {
74
+ this . factory = factory ;
75
+ this . items = new Element [ size - 1 ] ;
76
+ }
77
+
78
+ /// <summary>
79
+ /// Produces a <typeparamref name="T"/> instance.
80
+ /// </summary>
81
+ /// <returns>The returned <typeparamref name="T"/> item to use.</returns>
82
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
83
+ public T Allocate ( )
84
+ {
85
+ T ? item = this . firstItem ;
86
+
87
+ if ( item is null || item != Interlocked . CompareExchange ( ref this . firstItem , null , item ) )
88
+ {
89
+ item = AllocateSlow ( ) ;
90
+ }
91
+
92
+ return item ;
93
+ }
94
+
95
+ /// <summary>
96
+ /// Returns a given <typeparamref name="T"/> instance to the pool.
97
+ /// </summary>
98
+ /// <param name="obj">The <typeparamref name="T"/> instance to return.</param>
99
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
100
+ public void Free ( T obj )
101
+ {
102
+ if ( this . firstItem is null )
103
+ {
104
+ this . firstItem = obj ;
105
+ }
106
+ else
107
+ {
108
+ FreeSlow ( obj ) ;
109
+ }
110
+ }
111
+
112
+ /// <summary>
113
+ /// Allocates a new <typeparamref name="T"/> item.
114
+ /// </summary>
115
+ /// <returns>The returned <typeparamref name="T"/> item to use.</returns>
116
+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
117
+ private T AllocateSlow ( )
118
+ {
119
+ foreach ( ref Element element in this . items . AsSpan ( ) )
120
+ {
121
+ T ? instance = element . Value ;
122
+
123
+ if ( instance is not null )
124
+ {
125
+ if ( instance == Interlocked . CompareExchange ( ref element . Value , null , instance ) )
126
+ {
127
+ return instance ;
128
+ }
129
+ }
130
+ }
131
+
132
+ return this . factory ( ) ;
133
+ }
134
+
135
+ /// <summary>
136
+ /// Frees a given <typeparamref name="T"/> item.
137
+ /// </summary>
138
+ /// <param name="obj">The <typeparamref name="T"/> item to return to the pool.</param>
139
+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
140
+ private void FreeSlow ( T obj )
141
+ {
142
+ foreach ( ref Element element in this . items . AsSpan ( ) )
143
+ {
144
+ if ( element . Value is null )
145
+ {
146
+ element . Value = obj ;
147
+
148
+ break ;
149
+ }
150
+ }
151
+ }
152
+
153
+ /// <summary>
154
+ /// A container for a produced item (using a <see langword="struct"/> wrapper to avoid covariance checks).
155
+ /// </summary>
156
+ private struct Element
157
+ {
158
+ /// <summary>
159
+ /// The value held at the current element.
160
+ /// </summary>
161
+ internal T ? Value ;
162
+ }
163
+ }
0 commit comments