88final class ThreadLocalContextManager implements ContextManager {
99 static final ThreadLocalContextManager INSTANCE = new ThreadLocalContextManager ();
1010
11- private static final NoopContextContinuation ROOT_CONTINUATION =
12- new NoopContextContinuation (Context .root ());
13-
1411 private static final ThreadLocal <ContextHolder > CONTEXT_HOLDER =
1512 ThreadLocal .withInitial (ContextHolder ::new );
1613
14+ private static final NoopContextContinuation ROOT_CONTINUATION =
15+ new NoopContextContinuation (Context .root ());
16+
1717 private final Object listenersWriteLock = new Object ();
1818 volatile ContextListener [] listeners = {};
1919
@@ -40,16 +40,11 @@ ContextScope doAttach(Context context, @Nullable ContextContinuationImpl continu
4040 return context .asScope (); // convert to scope without attaching
4141 }
4242
43- ContextListener [] ls = listeners ;
44- notifyDetach (beforeAttach , ls );
4543 holder .current = context ;
46- notifyAttach (context , ls );
47-
48- if (continuation == null ) {
49- return new ContextScopeImpl (context , holder , beforeAttach );
50- } else {
51- return new ResumedScopeImpl (context , holder , beforeAttach , continuation );
52- }
44+ notifyUpdate (listeners , beforeAttach , context );
45+ return continuation == null
46+ ? new ContextScopeImpl (context , holder , beforeAttach )
47+ : new ResumedScopeImpl (context , holder , beforeAttach , continuation );
5348 }
5449
5550 @ Override
@@ -61,21 +56,14 @@ public Context swap(Context context) {
6156 return beforeSwap ;
6257 }
6358
64- ContextListener [] ls = listeners ;
65- notifyDetach (beforeSwap , ls );
6659 holder .current = context ;
67- notifyAttach (context , ls );
68-
60+ notifyUpdate (listeners , beforeSwap , context );
6961 return beforeSwap ;
7062 }
7163
7264 @ Override
7365 public ContextContinuation capture (Context context ) {
74- if (context == Context .root ()) {
75- return ROOT_CONTINUATION ;
76- } else {
77- return new ContextContinuationImpl (context );
78- }
66+ return context == Context .root () ? ROOT_CONTINUATION : new ContextContinuationImpl (context );
7967 }
8068
8169 @ Override
@@ -99,31 +87,16 @@ void clearListeners() {
9987 }
10088 }
10189
102- static void notifyAttach (Context context , ContextListener [] listeners ) {
103- if (context == Context .root ()) {
104- return ; // don't emit attach events for the default "no context" case
105- }
106- for (ContextListener l : listeners ) {
107- try {
108- l .onAttach (context );
109- } catch (Throwable ignore ) {
110- }
111- }
112- }
113-
114- static void notifyDetach (Context context , ContextListener [] listeners ) {
115- if (context == Context .root ()) {
116- return ; // don't emit detach events for the default "no context" case
117- }
90+ static void notifyUpdate (ContextListener [] listeners , Context before , Context after ) {
11891 for (ContextListener l : listeners ) {
11992 try {
120- l .onDetach ( context );
93+ l .onUpdate ( before , after );
12194 } catch (Throwable ignore ) {
12295 }
12396 }
12497 }
12598
126- static void notifyCapture (Context context , ContextListener [] listeners ) {
99+ static void notifyCapture (ContextListener [] listeners , Context context ) {
127100 // only called for non-empty continuations
128101 for (ContextListener l : listeners ) {
129102 try {
@@ -133,7 +106,7 @@ static void notifyCapture(Context context, ContextListener[] listeners) {
133106 }
134107 }
135108
136- static void notifyRelease (Context context , ContextListener [] listeners ) {
109+ static void notifyRelease (ContextListener [] listeners , Context context ) {
137110 // only called for non-empty continuations
138111 for (ContextListener l : listeners ) {
139112 try {
@@ -166,10 +139,8 @@ public final Context context() {
166139 public void close () {
167140 // check for out-of-order close to avoid corrupting the current state
168141 if (!closed && context == holder .current ) {
169- ContextListener [] ls = INSTANCE .listeners ;
170- notifyDetach (context , ls );
171142 holder .current = beforeAttach ;
172- notifyAttach ( beforeAttach , ls );
143+ notifyUpdate ( INSTANCE . listeners , context , beforeAttach );
173144 closed = true ;
174145 }
175146 }
@@ -232,7 +203,7 @@ private static final class ContextContinuationImpl implements ContextContinuatio
232203
233204 ContextContinuationImpl (Context context ) {
234205 this .context = context ;
235- notifyCapture (context , INSTANCE .listeners );
206+ notifyCapture (INSTANCE .listeners , context );
236207 }
237208
238209 @ Override
@@ -270,7 +241,7 @@ public void release() {
270241 while (current == 0 ) {
271242 // no outstanding resumes and hold has been removed
272243 if (COUNT .compareAndSet (this , current , RELEASED )) {
273- notifyRelease (context , INSTANCE .listeners );
244+ notifyRelease (INSTANCE .listeners , context );
274245 return ;
275246 }
276247 current = count ;
@@ -280,7 +251,7 @@ public void release() {
280251 void releaseOnScopeClose () {
281252 if (COUNT .compareAndSet (this , 1 , RELEASED )) {
282253 // fast path: only one resume of the continuation (no hold)
283- notifyRelease (context , INSTANCE .listeners );
254+ notifyRelease (INSTANCE .listeners , context );
284255 } else if (COUNT .decrementAndGet (this ) == 0 ) {
285256 // slow path: multiple resumes, all scopes now closed (no hold)
286257 release ();
0 commit comments