Skip to content

Commit

Permalink
Merge pull request quarkusio#22095 from mkouba/arc-cp-improvements
Browse files Browse the repository at this point in the history
ArcContextProvider optimizations
  • Loading branch information
Sanne committed Dec 10, 2021
2 parents 8bc2149 + 63d4f60 commit 621ce73
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
import io.quarkus.arc.ManagedContext;

/**
* Context propagation for Arc
* Only handles Request context as that's currently the only one in Arc that needs propagation.
* Context propagation for ArC.
* <p>
* Only handles the request context as that's currently the only one in ArC that needs propagation.
*/
public class ArcContextProvider implements ThreadContextProvider {

protected static final ThreadContextController NOOP_CONTROLLER = new ThreadContextController() {
@Override
public void endContext() throws IllegalStateException {

}
};

Expand All @@ -31,18 +31,14 @@ public void endContext() throws IllegalStateException {

@Override
public ThreadContextSnapshot currentContext(Map<String, String> map) {
ArcContainer arc = Arc.container();
if (arc == null || !arc.isRunning()) {
ArcContainer container = Arc.container();
if (container == null || !container.isRunning()) {
//return null as per docs to state that propagation of this context is not supported
return null;
}

// capture the state, null indicates no active context while capturing snapshot
InjectableContext.ContextState state = null;
ManagedContext requestContext = arc.requestContext();
if (requestContext.isActive()) {
state = requestContext.getState();
}
InjectableContext.ContextState state = container.requestContext().getStateIfActive();
if (state == null) {
return NULL_CONTEXT_SNAPSHOT;
} else {
Expand All @@ -53,8 +49,8 @@ public ThreadContextSnapshot currentContext(Map<String, String> map) {
@Override
public ThreadContextSnapshot clearedContext(Map<String, String> map) {
// note that by cleared we mean that we still activate context if need be, just leave the contents blank
ArcContainer arc = Arc.container();
if (arc == null || !arc.isRunning()) {
ArcContainer container = Arc.container();
if (container == null || !container.isRunning()) {
//return null as per docs to state that propagation of this context is not supported
return null;
}
Expand All @@ -71,18 +67,19 @@ private static final class ClearContextSnapshot implements ThreadContextSnapshot
@Override
public ThreadContextController begin() {
// can be called later on, we should retrieve the container again
ArcContainer arcContainer = Arc.container();
if (arcContainer == null || !arcContainer.isRunning()) {
ArcContainer container = Arc.container();
if (container == null || !container.isRunning()) {
return NOOP_CONTROLLER;
}
ManagedContext requestContext = arcContainer.requestContext();

ManagedContext requestContext = container.requestContext();
InjectableContext.ContextState toRestore = requestContext.getStateIfActive();
// this is executed on another thread, context can but doesn't need to be active here
if (requestContext.isActive()) {
if (toRestore != null) {
// context active, store current state, start blank context anew and restore state afterwards
InjectableContext.ContextState stateToRestore = requestContext.getState();
requestContext.deactivate();
// it is not necessary to deactivate the context first - just overwrite the previous state
requestContext.activate();
return new RestoreContextController(requestContext, stateToRestore);
return new RestoreContextController(requestContext, toRestore);
} else {
// context not active, activate blank one, deactivate afterwards
requestContext.activate();
Expand All @@ -96,22 +93,22 @@ private static final class NullContextSnapshot implements ThreadContextSnapshot
@Override
public ThreadContextController begin() {
// can be called later on, we should retrieve the container again
ArcContainer arcContainer = Arc.container();
if (arcContainer == null || !arcContainer.isRunning()) {
ArcContainer container = Arc.container();
if (container == null || !container.isRunning()) {
//this happens on shutdown, if we blow up here it can break shutdown, and stop
//resources from being cleaned up, causing tests to fail
return NOOP_CONTROLLER;
}
ManagedContext requestContext = arcContainer.requestContext();
ManagedContext requestContext = container.requestContext();
InjectableContext.ContextState toRestore = requestContext.getStateIfActive();
// this is executed on another thread, context can but doesn't need to be active here
if (requestContext.isActive()) {
if (toRestore != null) {
// context active, store current state, feed it new one and restore state afterwards
InjectableContext.ContextState stateToRestore = requestContext.getState();
requestContext.deactivate();
return new ThreadContextController() {
@Override
public void endContext() throws IllegalStateException {
requestContext.activate(stateToRestore);
requestContext.activate(toRestore);
}
};
} else {
Expand All @@ -132,20 +129,20 @@ public ContextSnapshot(ContextState state) {
@Override
public ThreadContextController begin() {
// can be called later on, we should retrieve the container again
ArcContainer arcContainer = Arc.container();
if (arcContainer == null || !arcContainer.isRunning()) {
ArcContainer container = Arc.container();
if (container == null || !container.isRunning()) {
//this happens on shutdown, if we blow up here it can break shutdown, and stop
//resources from being cleaned up, causing tests to fail
return NOOP_CONTROLLER;
}
ManagedContext requestContext = arcContainer.requestContext();
ManagedContext requestContext = container.requestContext();
InjectableContext.ContextState toRestore = requestContext.getStateIfActive();
// this is executed on another thread, context can but doesn't need to be active here
if (requestContext.isActive()) {
if (toRestore != null) {
// context active, store current state, feed it new one and restore state afterwards
InjectableContext.ContextState stateToRestore = requestContext.getState();
requestContext.deactivate();
// it is not necessary to deactivate the context first - just overwrite the previous state
requestContext.activate(state);
return new RestoreContextController(requestContext, stateToRestore);
return new RestoreContextController(requestContext, toRestore);
} else {
// context not active, activate and pass it new instance, deactivate afterwards
requestContext.activate(state);
Expand All @@ -155,7 +152,7 @@ public ThreadContextController begin() {

}

private static class RestoreContextController implements ThreadContextController {
private static final class RestoreContextController implements ThreadContextController {

private final ManagedContext requestContext;
private final InjectableContext.ContextState stateToRestore;
Expand All @@ -167,8 +164,7 @@ private static class RestoreContextController implements ThreadContextController

@Override
public void endContext() throws IllegalStateException {
// clean up, reactivate context with previous values
requestContext.deactivate();
// it is not necessary to deactivate the context first - just overwrite the previous state
requestContext.activate(stateToRestore);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.Map;
import java.util.function.Function;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.NormalScope;
import javax.enterprise.context.spi.AlterableContext;
import javax.enterprise.context.spi.Contextual;
Expand All @@ -21,9 +22,19 @@ public interface InjectableContext extends AlterableContext {

/**
* @return the current state
* @throws ContextNotActiveException
*/
ContextState getState();

/**
* If the context is active then return the current state.
*
* @return the current state or {@code null} if the context is not active
*/
default ContextState getStateIfActive() {
return isActive() ? getState() : null;
}

/**
* If the context is active then return an existing instance of certain contextual type or create a new instance, otherwise
* return a null value.
Expand Down Expand Up @@ -80,9 +91,7 @@ default boolean isNormal() {
interface ContextState {

/**
* The changes to the map are not reflected in the underlying context.
*
* @return a map of contextual instances
* @return an immutable map of contextual instances
*/
Map<InjectableBean<?>, Object> getContextualInstances();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,15 @@ public ContextState getState() {
return this;
}

@Override
public ContextState getStateIfActive() {
return this;
}

@Override
public Map<InjectableBean<?>, Object> getContextualInstances() {
return instances.getPresentValues().stream()
.collect(Collectors.toMap(ContextInstanceHandle::getBean, ContextInstanceHandle::get));
.collect(Collectors.toUnmodifiableMap(ContextInstanceHandle::getBean, ContextInstanceHandle::get));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,6 @@ public <T> InstanceHandle<T> instance(String name) {

@Override
public ManagedContext requestContext() {
requireRunning();
return requestContext;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ public ContextState getState() {
return new RequestContextState(ctx);
}

public ContextState getStateIfActive() {
ConcurrentMap<Contextual<?>, ContextInstanceHandle<?>> ctx = currentContext.get();
if (ctx == null) {
return null;
}
return new RequestContextState(ctx);
}

@Override
public void deactivate() {
currentContext.remove();
Expand Down Expand Up @@ -223,7 +231,7 @@ static class RequestContextState implements ContextState {
@Override
public Map<InjectableBean<?>, Object> getContextualInstances() {
return value.values().stream()
.collect(Collectors.toMap(ContextInstanceHandle::getBean, ContextInstanceHandle::get));
.collect(Collectors.toUnmodifiableMap(ContextInstanceHandle::getBean, ContextInstanceHandle::get));
}

}
Expand Down

0 comments on commit 621ce73

Please sign in to comment.