|
62 | 62 |
|
63 | 63 | import org.graalvm.collections.EconomicMap;
|
64 | 64 | import org.graalvm.collections.Pair;
|
| 65 | +import org.graalvm.nativeimage.ImageInfo; |
65 | 66 |
|
66 | 67 | import com.oracle.graal.python.PythonLanguage;
|
67 | 68 | import com.oracle.graal.python.builtins.PythonBuiltinClassType;
|
@@ -199,7 +200,16 @@ public final class CApiContext extends CExtContext {
|
199 | 200 | public Object timezoneType;
|
200 | 201 | private PyCapsule pyDateTimeCAPICapsule;
|
201 | 202 |
|
202 |
| - /** A cache for C symbols. */ |
| 203 | + /** |
| 204 | + * Same as {@link #nativeSymbolCache} if there is only one context per JVM (i.e. just one engine |
| 205 | + * in single-context mode). Will be {@code null} in case of multiple contexts. |
| 206 | + */ |
| 207 | + @CompilationFinal(dimensions = 1) private static Object[] nativeSymbolCacheSingleContext; |
| 208 | + private static final AtomicBoolean STATIC_SYMBOL_CACHE_USED = new AtomicBoolean(); |
| 209 | + |
| 210 | + /** |
| 211 | + * A private (i.e. per-context) cache of C API symbols (usually helper functions). |
| 212 | + */ |
203 | 213 | private final Object[] nativeSymbolCache;
|
204 | 214 |
|
205 | 215 | private record ClosureInfo(Object closure, Object delegate, Object executable, long pointer) {
|
@@ -242,6 +252,33 @@ public CApiContext(PythonContext context, Object llvmLibrary, boolean useNativeB
|
242 | 252 | super(context, llvmLibrary, useNativeBackend);
|
243 | 253 | this.nativeSymbolCache = new Object[NativeCAPISymbol.values().length];
|
244 | 254 |
|
| 255 | + /* |
| 256 | + * Publish the native symbol cache to the static field if following is given: (1) The static |
| 257 | + * field hasn't been used by another instance yet (i.e. '!used'), and (2) we are in |
| 258 | + * single-context mode. This initialization ensures that if |
| 259 | + * 'CApiContext.nativeSymbolCacheSingleContext != null', the context is safe to use it and |
| 260 | + * just needs to do a null check. |
| 261 | + */ |
| 262 | + if (!STATIC_SYMBOL_CACHE_USED.compareAndExchange(false, true) && context.getLanguage().isSingleContext()) { |
| 263 | + assert CApiContext.nativeSymbolCacheSingleContext == null; |
| 264 | + |
| 265 | + // we cannot be in built-time code because this is using pre-initialized contexts |
| 266 | + assert !ImageInfo.inImageBuildtimeCode(); |
| 267 | + |
| 268 | + // this is the first context accessing the static symbol cache |
| 269 | + CApiContext.nativeSymbolCacheSingleContext = this.nativeSymbolCache; |
| 270 | + } else if (CApiContext.nativeSymbolCacheSingleContext != null) { |
| 271 | + /* |
| 272 | + * In this case, this context instance is at least the second one attempting to use the |
| 273 | + * static symbol cache. We now clear the static field to indicate that every context |
| 274 | + * instance should use its private cache. If a former context already used the cache and |
| 275 | + * there is already compiled code, it is not necessary to invalidate the code because |
| 276 | + * the cache is still valid. |
| 277 | + */ |
| 278 | + CApiContext.nativeSymbolCacheSingleContext = null; |
| 279 | + } |
| 280 | + assert STATIC_SYMBOL_CACHE_USED.get(); |
| 281 | + |
245 | 282 | // initialize primitive native wrapper cache
|
246 | 283 | primitiveNativeWrapperCache = new PrimitiveNativeWrapper[262];
|
247 | 284 | for (int i = 0; i < primitiveNativeWrapperCache.length; i++) {
|
@@ -391,19 +428,44 @@ public Object getModuleByIndex(int i) {
|
391 | 428 | return null;
|
392 | 429 | }
|
393 | 430 |
|
394 |
| - Object getNativeSymbol(NativeCAPISymbol symbol) { |
| 431 | + /** |
| 432 | + * Retrieves the C API symbol cache instance in the fastest possible way. If there is just one |
| 433 | + * instance of {@link CApiContext}, it will load the cache stored from the static field |
| 434 | + * {@link CApiContext#nativeSymbolCacheSingleContext}. Otherwise, it will load the cache from |
| 435 | + * the instance field {@link CApiContext#nativeSymbolCache}. |
| 436 | + * |
| 437 | + * @param caller The requesting node (may be {@code null}). Used for the fast-path lookup of the |
| 438 | + * {@link CApiContext} instance (if necessary). |
| 439 | + * @return The C API symbol cache. |
| 440 | + */ |
| 441 | + private static Object[] getSymbolCache(Node caller) { |
| 442 | + Object[] nativeSymbolCacheSingleContext = CApiContext.nativeSymbolCacheSingleContext; |
| 443 | + if (nativeSymbolCacheSingleContext != null) { |
| 444 | + return nativeSymbolCacheSingleContext; |
| 445 | + } |
| 446 | + return PythonContext.get(caller).getCApiContext().nativeSymbolCache; |
| 447 | + } |
| 448 | + |
| 449 | + static Object getNativeSymbol(Node caller, NativeCAPISymbol symbol) { |
| 450 | + Object[] nativeSymbolCache = getSymbolCache(caller); |
395 | 451 | Object result = nativeSymbolCache[symbol.ordinal()];
|
396 |
| - if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, result == null)) { |
397 |
| - result = lookupNativeSymbol(symbol); |
| 452 | + if (result == null) { |
| 453 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 454 | + result = lookupNativeSymbol(nativeSymbolCache, symbol); |
398 | 455 | }
|
| 456 | + assert result != null; |
399 | 457 | return result;
|
400 | 458 | }
|
401 | 459 |
|
402 |
| - @TruffleBoundary |
403 |
| - private Object lookupNativeSymbol(NativeCAPISymbol symbol) { |
| 460 | + /** |
| 461 | + * Lookup the given C API symbol in the library, store it to the provided cache, and return the |
| 462 | + * callable symbol. |
| 463 | + */ |
| 464 | + private static Object lookupNativeSymbol(Object[] nativeSymbolCache, NativeCAPISymbol symbol) { |
| 465 | + CompilerAsserts.neverPartOfCompilation(); |
404 | 466 | String name = symbol.getName();
|
405 | 467 | try {
|
406 |
| - Object nativeSymbol = InteropLibrary.getUncached().readMember(getLLVMLibrary(), name); |
| 468 | + Object nativeSymbol = InteropLibrary.getUncached().readMember(PythonContext.get(null).getCApiContext().getLLVMLibrary(), name); |
407 | 469 | return nativeSymbolCache[symbol.ordinal()] = CExtContext.ensureExecutable(nativeSymbol, symbol);
|
408 | 470 | } catch (UnsupportedMessageException | UnknownIdentifierException e) {
|
409 | 471 | throw CompilerDirectives.shouldNotReachHere(e);
|
|
0 commit comments