|
5 | 5 | using System; |
6 | 6 | using System.Diagnostics; |
7 | 7 | using System.Threading; |
8 | | -using System.Threading.Channels; |
9 | | -using System.Threading.Tasks; |
10 | 8 | using Microsoft.Data.ProviderBase; |
11 | 9 |
|
12 | 10 | #nullable enable |
13 | 11 |
|
14 | 12 | namespace Microsoft.Data.SqlClient.ConnectionPool |
15 | 13 | { |
16 | 14 | /// <summary> |
17 | | - /// A thread-safe collection with a fixed capacity that allows reservations. |
18 | | - /// A reservation *must* be made before adding a connection to the collection. |
19 | | - /// Exceptions *must* be handled by the caller when trying to add connections |
20 | | - /// and the caller *must* release the reservation. |
| 15 | + /// A thread-safe collection with a fixed capacity. Avoids wasted work by reserving a slot before adding an item. |
21 | 16 | /// </summary> |
22 | | - /// <example> |
23 | | - /// <code> |
24 | | - /// ConnectionPoolSlots slots = new ConnectionPoolSlots(100); |
25 | | - /// |
26 | | - /// if (slots.TryReserve()) |
27 | | - /// { |
28 | | - /// try { |
29 | | - /// var connection = OpenConnection(); |
30 | | - /// slots.Add(connection); |
31 | | - /// } |
32 | | - /// catch (InvalidOperationException ex) |
33 | | - /// { |
34 | | - /// slots.ReleaseReservation(); |
35 | | - /// throw; |
36 | | - /// } |
37 | | - /// } |
38 | | - /// |
39 | | - /// if (slots.TryRemove()) |
40 | | - /// { |
41 | | - /// slots.ReleaseReservation(); |
42 | | - /// } |
43 | | - /// |
44 | | - /// </code> |
45 | | - /// </example> |
46 | 17 | internal sealed class ConnectionPoolSlots |
47 | 18 | { |
48 | | - |
| 19 | + /// <summary> |
| 20 | + /// Represents a reservation that manages a resource and ensures cleanup when no longer needed. |
| 21 | + /// </summary> |
| 22 | + /// <typeparam name="T">The type of the resource being managed by the reservation.</typeparam> |
49 | 23 | private sealed class Reservation<T> : IDisposable |
50 | 24 | { |
51 | 25 | private Action<T> cleanupCallback; |
@@ -91,29 +65,46 @@ internal void Keep() |
91 | 65 | /// Constructs a ConnectionPoolSlots instance with the given fixed capacity. |
92 | 66 | /// </summary> |
93 | 67 | /// <param name="fixedCapacity">The fixed capacity of the collection.</param> |
| 68 | + /// <exception cref="ArgumentOutOfRangeException"> |
| 69 | + /// Thrown when fixedCapacity is greater than Int32.MaxValue or equal to zero. |
| 70 | + /// </exception> |
94 | 71 | internal ConnectionPoolSlots(uint fixedCapacity) |
95 | 72 | { |
| 73 | + if (fixedCapacity > int.MaxValue) |
| 74 | + { |
| 75 | + throw new ArgumentOutOfRangeException(nameof(fixedCapacity), "Capacity must be less than or equal to Int32.MaxValue."); |
| 76 | + } |
| 77 | + |
| 78 | + if (fixedCapacity == 0) |
| 79 | + { |
| 80 | + throw new ArgumentOutOfRangeException(nameof(fixedCapacity), "Capacity must be greater than zero."); |
| 81 | + } |
| 82 | + |
96 | 83 | _capacity = fixedCapacity; |
97 | 84 | _reservations = 0; |
98 | 85 | _connections = new DbConnectionInternal?[fixedCapacity]; |
99 | 86 | } |
100 | 87 |
|
101 | 88 | /// <summary> |
102 | | - /// Gets the total number of reservations. |
| 89 | + /// Gets the total number of reservations currently held. |
103 | 90 | /// </summary> |
104 | 91 | internal int ReservationCount => _reservations; |
105 | 92 |
|
106 | 93 | /// <summary> |
107 | | - /// Adds a connection to the collection. Can only be called after a reservation has been made. |
| 94 | + /// Adds a connection to the collection. |
108 | 95 | /// </summary> |
109 | | - /// <param name="createCallback">The connection to add to the collection.</param> |
110 | | - /// <param name="cleanupCallback">Callback to clean up resources if an exception occurs.</param> |
| 96 | + /// <param name="createCallback">Callback that provides the connection to add to the collection. This callback |
| 97 | + /// *must not* call any other ConnectionPoolSlots methods.</param> |
| 98 | + /// <param name="cleanupCallback">Callback to clean up resources if an exception occurs. This callback *must |
| 99 | + /// not* call any other ConnectionPoolSlots methods.</param> |
111 | 100 | /// <param name="createState">State made available to the create callback.</param> |
112 | 101 | /// <param name="cleanupState">State made available to the cleanup callback.</param> |
113 | | - /// <exception cref="InvalidOperationException"> |
114 | | - /// Thrown when unable to find an empty slot. |
115 | | - /// This can occur if a reservation is not taken before adding a connection. |
| 102 | + /// <exception cref="Exception"> |
| 103 | + /// Throws when createCallback throws an exception. |
| 104 | + /// Throws when a reservation is successfully made, but an empty slot cannot be found. This condition is |
| 105 | + /// unexpected and indicates a bug. |
116 | 106 | /// </exception> |
| 107 | + /// <returns>Returns the new connection, or null if there was not available space.</returns> |
117 | 108 | internal DbConnectionInternal? Add<T, S>( |
118 | 109 | CreateCallback<DbConnectionInternal?, T> createCallback, |
119 | 110 | CleanupCallback<S> cleanupCallback, |
@@ -186,7 +177,7 @@ internal bool TryRemove(DbConnectionInternal connection) |
186 | 177 | /// <summary> |
187 | 178 | /// Attempts to reserve a spot in the collection. |
188 | 179 | /// </summary> |
189 | | - /// <returns>True if a reservation was successfully obtained.</returns> |
| 180 | + /// <returns>A Reservation if successful, otherwise returns null.</returns> |
190 | 181 | private Reservation<ConnectionPoolSlots>? TryReserve() |
191 | 182 | { |
192 | 183 | for (var expected = _reservations; expected < _capacity; expected = _reservations) |
|
0 commit comments