@@ -59,7 +59,7 @@ private Context() {
5959 * @param attr Context Attribute.
6060 * @return Context Attribute Value.
6161 */
62- public static @ Nullable <T > T get (ContextAttribute <T > attr ) {
62+ @ Nullable public static <T > T get (ContextAttribute <T > attr ) {
6363 AttributeValueHolder <T > valHolder = INSTANCE .get ().attributeValue (attr );
6464
6565 return valHolder == null ? attr .initialValue () : valHolder .value ();
@@ -69,11 +69,15 @@ private Context() {
6969 * Updates the value of the specified attribute for the Context bound to the thread this method is called from.
7070 *
7171 * @param attr Context Attribute.
72- * @return Scope instance that, when closed, undoes the applied update. Note, updates must be undone in the same
73- * order they were applied. It is highly encouraged to use try-with-resource block to close a Scope.
72+ * @return Scope instance that, when closed, undoes the applied update. It is crucial to undo all applied Context
73+ * updates to free up thread-bound resources and avoid memory leaks, so it is highly encouraged to use a
74+ * try-with-resource block to close the returned Scope. Note, updates must be undone in the same order they were applied.
7475 */
7576 public static <T > Scope set (ContextAttribute <T > attr , T val ) {
76- return UpdateHelper .create ().set (attr , val ).apply ();
77+ if (get (attr ) == val )
78+ return Scope .NOOP_SCOPE ;
79+
80+ return INSTANCE .get ().applyUpdates (new AttributeValueHolder <>(attr , val ));
7781 }
7882
7983 /**
@@ -83,14 +87,15 @@ public static <T> Scope set(ContextAttribute<T> attr, T val) {
8387 * @param val1 Values associated with first Context Attribute.
8488 * @param attr2 Second Context Attribute.
8589 * @param val2 Values associated with second Context Attribute.
86- * @return Scope instance that, when closed, undoes the applied update. Note, updates must be undone in the same
87- * order they were applied. It is highly encouraged to use try-with-resource block to close a Scope.
90+ * @return Scope instance that, when closed, undoes the applied update. It is crucial to undo all applied Context
91+ * updates to free up thread-bound resources and avoid memory leaks, so it is highly encouraged to use a
92+ * try-with-resource block to close the returned Scope. Note, updates must be undone in the same order they were applied.
8893 */
8994 public static <T1 , T2 > Scope set (
9095 ContextAttribute <T1 > attr1 , T1 val1 ,
9196 ContextAttribute <T2 > attr2 , T2 val2
9297 ) {
93- return UpdateHelper .create ().set (attr1 , val1 ).set (attr2 , val2 ).apply ();
98+ return ContextUpdater .create ().set (attr1 , val1 ).set (attr2 , val2 ).apply ();
9499 }
95100
96101 /**
@@ -102,15 +107,16 @@ public static <T1, T2> Scope set(
102107 * @param val2 Values associated with second Context Attribute.
103108 * @param attr3 Third Context Attribute.
104109 * @param val3 Values associated with third Context Attribute.
105- * @return Scope instance that, when closed, undoes the applied update. Note, updates must be undone in the same
106- * order they were applied. It is highly encouraged to use try-with-resource block to close a Scope.
110+ * @return Scope instance that, when closed, undoes the applied update. It is crucial to undo all applied Context
111+ * updates to free up thread-bound resources and avoid memory leaks, so it is highly encouraged to use a
112+ * try-with-resource block to close the returned Scope. Note, updates must be undone in the same order they were applied.
107113 */
108114 public static <T1 , T2 , T3 > Scope set (
109115 ContextAttribute <T1 > attr1 , T1 val1 ,
110116 ContextAttribute <T2 > attr2 , T2 val2 ,
111117 ContextAttribute <T3 > attr3 , T3 val3
112118 ) {
113- return UpdateHelper .create ().set (attr1 , val1 ).set (attr2 , val2 ).set (attr3 , val3 ).apply ();
119+ return ContextUpdater .create ().set (attr1 , val1 ).set (attr2 , val2 ).set (attr3 , val3 ).apply ();
114120 }
115121
116122 /**
@@ -127,8 +133,9 @@ public static ContextSnapshot createSnapshot() {
127133 * Restores values of all attributes for Context bound to the thread this method is called from.
128134 *
129135 * @param snp Context Snapshot.
130- * @return Scope instance that, when closed, undoes the applied update. Note, updates must be undone in the same
131- * order they were applied. It is highly encouraged to use try-with-resource block to close a Scope.
136+ * @return Scope instance that, when closed, undoes the applied operation. It is crucial to undo all applied Context
137+ * updates to free up thread-bound resources and avoid memory leaks, so it is highly encouraged to use a
138+ * try-with-resource block to close the returned Scope. Note, updates must be undone in the same order they were applied.
132139 */
133140 public static Scope restoreSnapshot (ContextSnapshot snp ) {
134141 return INSTANCE .get ().restoreSnapshotInternal (snp );
@@ -148,7 +155,7 @@ public static Scope restoreSnapshot(ContextSnapshot snp) {
148155 }
149156
150157 /** Updates the current context with the specified attributes and their corresponding values. */
151- private Scope applyUpdates (AttributeValueHolder <?>[] atrVals ) {
158+ private Scope applyUpdates (AttributeValueHolder <?>... atrVals ) {
152159 lastUpd = new Update (atrVals , lastUpd );
153160
154161 return lastUpd ;
@@ -245,7 +252,11 @@ boolean holdsValueFor(ContextAttribute<?> attr) {
245252 * specified Attribute was not changed by this update.
246253 */
247254 @ Nullable <T > AttributeValueHolder <T > value (ContextAttribute <T > attr ) {
248- for (AttributeValueHolder <?> valHolder : attrVals ) {
255+ // We iterate in reverse order to correctly handle the case when the value for the same attribute is
256+ // specified multiple times.
257+ for (int i = attrVals .length - 1 ; i >= 0 ; i --) {
258+ AttributeValueHolder <?> valHolder = attrVals [i ];
259+
249260 if (valHolder .attribute ().equals (attr ))
250261 return ((AttributeValueHolder <T >)valHolder );
251262 }
@@ -271,22 +282,22 @@ private static int mergeUpdatedAttributeBits(AttributeValueHolder<?>[] attrVals)
271282
272283 /**
273284 * Helps to change multiple Attribute values in a single update and to skip updates that changes nothing. */
274- private static class UpdateHelper {
285+ private static class ContextUpdater {
275286 /** */
276287 private static final int INIT_UPDATES_CAPACITY = 3 ;
277288
278289 /** */
279290 private List <AttributeValueHolder <?>> updates ;
280291
281292 /** */
282- <T > UpdateHelper set (ContextAttribute <T > attr , T val ) {
293+ <T > ContextUpdater set (ContextAttribute <T > attr , T val ) {
283294 if (get (attr ) == val )
284295 return this ;
285296
286297 if (updates == null )
287298 updates = new ArrayList <>(INIT_UPDATES_CAPACITY );
288299
289- enlistUpdate (new AttributeValueHolder <>(attr , val ));
300+ updates . add (new AttributeValueHolder <>(attr , val ));
290301
291302 return this ;
292303 }
@@ -304,23 +315,8 @@ Scope apply() {
304315 }
305316
306317 /** */
307- static UpdateHelper create () {
308- return new UpdateHelper ();
309- }
310-
311- /** */
312- private <T > void enlistUpdate (AttributeValueHolder <?> valHolder ) {
313- for (int i = 0 ; i < updates .size (); i ++) {
314- AttributeValueHolder <?> upd = updates .get (i );
315-
316- if (upd .attribute ().equals (valHolder .attribute ())) {
317- updates .set (i , valHolder );
318-
319- return ;
320- }
321- }
322-
323- updates .add (valHolder );
318+ static ContextUpdater create () {
319+ return new ContextUpdater ();
324320 }
325321 }
326322}
0 commit comments