Skip to content

Commit 8de9656

Browse files
authored
Merge pull request #5048 from eclipse-vertx/context-local-data-rework
Context local storage SPI
2 parents 582d864 + af9fa82 commit 8de9656

File tree

21 files changed

+613
-113
lines changed

21 files changed

+613
-113
lines changed

pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,21 @@
724724
</additionalClasspathElements>
725725
</configuration>
726726
</execution>
727+
<execution>
728+
<id>custom-context-local</id>
729+
<goals>
730+
<goal>integration-test</goal>
731+
<goal>verify</goal>
732+
</goals>
733+
<configuration>
734+
<includes>
735+
<include>io/vertx/it/CustomContextLocalTest.java</include>
736+
</includes>
737+
<additionalClasspathElements>
738+
<additionalClasspathElement>${project.basedir}/src/test/classpath/customcontextlocal</additionalClasspathElement>
739+
</additionalClasspathElements>
740+
</configuration>
741+
</execution>
727742
</executions>
728743
</plugin>
729744

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.impl;
12+
13+
import io.vertx.core.spi.context.storage.AccessMode;
14+
import io.vertx.core.spi.context.storage.ContextLocal;
15+
16+
import java.util.function.Supplier;
17+
18+
/**
19+
* Base class for context.
20+
*
21+
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
22+
*/
23+
class ContextBase {
24+
25+
final Object[] locals;
26+
27+
ContextBase(Object[] locals) {
28+
this.locals = locals;
29+
}
30+
31+
public final <T> T getLocal(ContextLocal<T> key, AccessMode accessMode) {
32+
ContextLocalImpl<T> internalKey = (ContextLocalImpl<T>) key;
33+
int index = internalKey.index;
34+
if (index >= locals.length) {
35+
throw new IllegalArgumentException();
36+
}
37+
Object res = accessMode.get(locals, index);
38+
return (T) res;
39+
}
40+
41+
public final <T> T getLocal(ContextLocal<T> key, AccessMode accessMode, Supplier<? extends T> initialValueSupplier) {
42+
ContextLocalImpl<T> internalKey = (ContextLocalImpl<T>) key;
43+
int index = internalKey.index;
44+
if (index >= locals.length) {
45+
throw new IllegalArgumentException("Invalid key index: " + index);
46+
}
47+
Object res = accessMode.getOrCreate(locals, index, (Supplier<Object>) initialValueSupplier);
48+
return (T) res;
49+
}
50+
51+
public final <T> void putLocal(ContextLocal<T> key, AccessMode accessMode, T value) {
52+
ContextLocalImpl<T> internalKey = (ContextLocalImpl<T>) key;
53+
int index = internalKey.index;
54+
if (index >= locals.length) {
55+
throw new IllegalArgumentException();
56+
}
57+
accessMode.put(locals, index, value);
58+
}
59+
}

src/main/java/io/vertx/core/impl/ContextImpl.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* @author <a href="http://tfox.org">Tim Fox</a>
2929
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
3030
*/
31-
public final class ContextImpl implements ContextInternal {
31+
public final class ContextImpl extends ContextBase implements ContextInternal {
3232

3333
private static final Logger log = LoggerFactory.getLogger(ContextImpl.class);
3434

@@ -44,14 +44,14 @@ public final class ContextImpl implements ContextInternal {
4444
private final EventLoop eventLoop;
4545
private final EventExecutor executor;
4646
private ConcurrentMap<Object, Object> data;
47-
private ConcurrentMap<Object, Object> localData;
4847
private volatile Handler<Throwable> exceptionHandler;
4948
final TaskQueue internalOrderedTasks;
5049
final WorkerPool internalWorkerPool;
5150
final WorkerPool workerPool;
5251
final TaskQueue orderedTasks;
5352

5453
protected ContextImpl(VertxInternal vertx,
54+
Object[] locals,
5555
ThreadingModel threadingModel,
5656
EventLoop eventLoop,
5757
EventExecutor executor,
@@ -61,6 +61,7 @@ protected ContextImpl(VertxInternal vertx,
6161
Deployment deployment,
6262
CloseFuture closeFuture,
6363
ClassLoader tccl) {
64+
super(locals);
6465
this.threadingModel = threadingModel;
6566
this.deployment = deployment;
6667
this.config = deployment != null ? deployment.config() : new JsonObject();
@@ -211,14 +212,6 @@ public synchronized ConcurrentMap<Object, Object> contextData() {
211212
return data;
212213
}
213214

214-
@Override
215-
public synchronized ConcurrentMap<Object, Object> localContextData() {
216-
if (localData == null) {
217-
localData = new ConcurrentHashMap<>();
218-
}
219-
return localData;
220-
}
221-
222215
public void reportException(Throwable t) {
223216
Handler<Throwable> handler = exceptionHandler;
224217
if (handler == null) {
@@ -306,6 +299,6 @@ protected <T> void emit(ContextInternal ctx, T argument, Handler<T> task) {
306299

307300
@Override
308301
public ContextInternal duplicate() {
309-
return new DuplicatedContext(this);
302+
return new DuplicatedContext(this, locals.length == 0 ? VertxImpl.EMPTY_CONTEXT_LOCALS : new Object[locals.length]);
310303
}
311304
}

src/main/java/io/vertx/core/impl/ContextInternal.java

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@
1313

1414
import io.netty.channel.EventLoop;
1515
import io.vertx.core.*;
16+
import io.vertx.core.Future;
1617
import io.vertx.core.impl.future.FailedFuture;
1718
import io.vertx.core.impl.future.PromiseImpl;
1819
import io.vertx.core.impl.future.PromiseInternal;
1920
import io.vertx.core.impl.future.SucceededFuture;
21+
import io.vertx.core.spi.context.storage.AccessMode;
22+
import io.vertx.core.spi.context.storage.ContextLocal;
2023
import io.vertx.core.spi.tracing.VertxTracer;
2124

22-
import java.util.concurrent.Callable;
23-
import java.util.concurrent.ConcurrentMap;
24-
import java.util.concurrent.Executor;
25-
import java.util.concurrent.TimeUnit;
25+
import java.util.concurrent.*;
26+
import java.util.function.Supplier;
2627

2728
/**
2829
* This interface provides an api for vert.x core internal use only
@@ -33,6 +34,8 @@
3334
*/
3435
public interface ContextInternal extends Context {
3536

37+
ContextLocal<ConcurrentMap<Object, Object>> LOCAL_MAP = new ContextLocalImpl<>(0);
38+
3639
/**
3740
* @return the current context
3841
*/
@@ -304,19 +307,76 @@ default boolean remove(Object key) {
304307
/**
305308
* @return the {@link ConcurrentMap} used to store local context data
306309
*/
307-
ConcurrentMap<Object, Object> localContextData();
310+
default ConcurrentMap<Object, Object> localContextData() {
311+
return LOCAL_MAP.get(this, ConcurrentHashMap::new);
312+
}
313+
314+
/**
315+
* Get some local data from the context.
316+
*
317+
* @param key the key of the data
318+
* @param <T> the type of the data
319+
* @return the local data
320+
*/
321+
default <T> T getLocal(ContextLocal<T> key) {
322+
return getLocal(key, AccessMode.CONCURRENT);
323+
}
324+
325+
/**
326+
* Get some local data from the context.
327+
*
328+
* @param key the key of the data
329+
* @param <T> the type of the data
330+
* @return the local data
331+
*/
332+
<T> T getLocal(ContextLocal<T> key, AccessMode accessMode);
308333

334+
/**
335+
* Get some local data from the context, when it does not exist the {@code initialValueSupplier} is called to obtain
336+
* the initial value.
337+
*
338+
* <p> The {@code initialValueSupplier} might be called multiple times when multiple threads call this method concurrently.
339+
*
340+
* @param key the key of the data
341+
* @param initialValueSupplier the supplier of the initial value optionally called
342+
* @param <T> the type of the data
343+
* @return the local data
344+
*/
345+
<T> T getLocal(ContextLocal<T> key, AccessMode accessMode, Supplier<? extends T> initialValueSupplier);
346+
347+
/**
348+
* Put some local data in the context.
349+
* <p>
350+
* This can be used to share data between different handlers that share a context
351+
*
352+
* @param key the key of the data
353+
* @param value the data
354+
*/
355+
<T> void putLocal(ContextLocal<T> key, AccessMode accessMode, T value);
356+
357+
/**
358+
* Remove some local data from the context.
359+
*
360+
* @param key the key to remove
361+
*/
362+
default <T> void removeLocal(ContextLocal<T> key, AccessMode accessMode) {
363+
putLocal(key, accessMode, null);
364+
}
365+
366+
@Deprecated
309367
@SuppressWarnings("unchecked")
310368
@Override
311369
default <T> T getLocal(Object key) {
312370
return (T) localContextData().get(key);
313371
}
314372

373+
@Deprecated
315374
@Override
316375
default void putLocal(Object key, Object value) {
317376
localContextData().put(key, value);
318377
}
319378

379+
@Deprecated
320380
@Override
321381
default boolean removeLocal(Object key) {
322382
return localContextData().remove(key) != null;
@@ -445,4 +505,5 @@ default ContextInternal unwrap() {
445505
default boolean isDuplicate() {
446506
return false;
447507
}
508+
448509
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.impl;
12+
13+
import io.vertx.core.spi.context.storage.ContextLocal;
14+
15+
/**
16+
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
17+
*/
18+
public class ContextLocalImpl<T> implements ContextLocal<T> {
19+
20+
final int index;
21+
22+
public ContextLocalImpl(int index) {
23+
this.index = index;
24+
}
25+
26+
public ContextLocalImpl() {
27+
this.index = LocalSeq.next();
28+
}
29+
}

src/main/java/io/vertx/core/impl/DuplicatedContext.java

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
*
3535
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
3636
*/
37-
class DuplicatedContext implements ContextInternal {
37+
final class DuplicatedContext extends ContextBase implements ContextInternal {
3838

39-
protected final ContextImpl delegate;
40-
private ConcurrentMap<Object, Object> localData;
39+
final ContextImpl delegate;
4140

42-
DuplicatedContext(ContextImpl delegate) {
41+
DuplicatedContext(ContextImpl delegate, Object[] locals) {
42+
super(locals);
4343
this.delegate = delegate;
4444
}
4545

@@ -119,16 +119,6 @@ public final ConcurrentMap<Object, Object> contextData() {
119119
return delegate.contextData();
120120
}
121121

122-
@Override
123-
public final ConcurrentMap<Object, Object> localContextData() {
124-
synchronized (this) {
125-
if (localData == null) {
126-
localData = new ConcurrentHashMap<>();
127-
}
128-
return localData;
129-
}
130-
}
131-
132122
@Override
133123
public <T> Future<T> executeBlockingInternal(Callable<T> action) {
134124
return ContextImpl.executeBlocking(this, action, delegate.internalWorkerPool, delegate.internalOrderedTasks);
@@ -176,7 +166,7 @@ public boolean isWorkerContext() {
176166

177167
@Override
178168
public ContextInternal duplicate() {
179-
return new DuplicatedContext(delegate);
169+
return new DuplicatedContext(delegate, locals.length == 0 ? VertxImpl.EMPTY_CONTEXT_LOCALS : new Object[locals.length]);
180170
}
181171

182172
@Override
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.impl;
12+
13+
import java.util.concurrent.atomic.AtomicInteger;
14+
15+
/**
16+
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
17+
*/
18+
class LocalSeq {
19+
20+
// 0 : reserved slot for local context map
21+
private static final AtomicInteger seq = new AtomicInteger(1);
22+
23+
/**
24+
* Hook for testing purposes
25+
*/
26+
static void reset() {
27+
seq.set((1));
28+
}
29+
30+
static int get() {
31+
return seq.get();
32+
}
33+
34+
static int next() {
35+
return seq.getAndIncrement();
36+
}
37+
}

0 commit comments

Comments
 (0)