|
48 | 48 |
|
49 | 49 | import java.io.IOException;
|
50 | 50 | import java.io.PrintStream;
|
| 51 | +import java.lang.invoke.VarHandle; |
51 | 52 | import java.nio.file.LinkOption;
|
52 | 53 | import java.util.ArrayList;
|
53 | 54 | import java.util.Arrays;
|
|
62 | 63 |
|
63 | 64 | import org.graalvm.collections.EconomicMap;
|
64 | 65 | import org.graalvm.collections.Pair;
|
| 66 | +import org.graalvm.nativeimage.ImageInfo; |
65 | 67 |
|
66 | 68 | import com.oracle.graal.python.PythonLanguage;
|
67 | 69 | import com.oracle.graal.python.builtins.PythonBuiltinClassType;
|
|
126 | 128 | import com.oracle.truffle.api.library.ExportLibrary;
|
127 | 129 | import com.oracle.truffle.api.library.ExportMessage;
|
128 | 130 | import com.oracle.truffle.api.nodes.Node;
|
129 |
| -import com.oracle.truffle.api.object.DynamicObjectLibrary; |
130 |
| -import com.oracle.truffle.api.object.Shape; |
131 | 131 | import com.oracle.truffle.api.source.Source;
|
132 | 132 | import com.oracle.truffle.api.source.Source.SourceBuilder;
|
133 | 133 | import com.oracle.truffle.api.strings.TruffleString;
|
@@ -201,6 +201,18 @@ public final class CApiContext extends CExtContext {
|
201 | 201 | public Object timezoneType;
|
202 | 202 | private PyCapsule pyDateTimeCAPICapsule;
|
203 | 203 |
|
| 204 | + /** |
| 205 | + * Same as {@link #nativeSymbolCache} if there is only one context per JVM (i.e. just one engine |
| 206 | + * in single-context mode). Will be {@code null} in case of multiple contexts. |
| 207 | + */ |
| 208 | + @CompilationFinal(dimensions = 1) private static Object[] nativeSymbolCacheSingleContext; |
| 209 | + private static boolean nativeSymbolCacheSingleContextUsed; |
| 210 | + |
| 211 | + /** |
| 212 | + * A private (i.e. per-context) cache of C API symbols (usually helper functions). |
| 213 | + */ |
| 214 | + private final Object[] nativeSymbolCache; |
| 215 | + |
204 | 216 | private record ClosureInfo(Object closure, Object delegate, Object executable, long pointer) {
|
205 | 217 | }
|
206 | 218 |
|
@@ -239,6 +251,37 @@ public static TruffleLogger getLogger(Class<?> clazz) {
|
239 | 251 |
|
240 | 252 | public CApiContext(PythonContext context, Object llvmLibrary, boolean useNativeBackend) {
|
241 | 253 | super(context, llvmLibrary, useNativeBackend);
|
| 254 | + this.nativeSymbolCache = new Object[NativeCAPISymbol.values().length]; |
| 255 | + |
| 256 | + /* |
| 257 | + * Publish the native symbol cache to the static field if following is given: (1) The static |
| 258 | + * field hasn't been used by another instance yet (i.e. '!used'), and (2) we are in |
| 259 | + * single-context mode. This initialization ensures that if |
| 260 | + * 'CApiContext.nativeSymbolCacheSingleContext != null', the context is safe to use it and |
| 261 | + * just needs to do a null check. |
| 262 | + */ |
| 263 | + synchronized (CApiContext.class) { |
| 264 | + if (!CApiContext.nativeSymbolCacheSingleContextUsed && context.getLanguage().isSingleContext()) { |
| 265 | + assert CApiContext.nativeSymbolCacheSingleContext == null; |
| 266 | + |
| 267 | + // we cannot be in built-time code because this is using pre-initialized contexts |
| 268 | + assert !ImageInfo.inImageBuildtimeCode(); |
| 269 | + |
| 270 | + // this is the first context accessing the static symbol cache |
| 271 | + CApiContext.nativeSymbolCacheSingleContext = this.nativeSymbolCache; |
| 272 | + } else if (CApiContext.nativeSymbolCacheSingleContext != null) { |
| 273 | + assert CApiContext.nativeSymbolCacheSingleContextUsed; |
| 274 | + /* |
| 275 | + * In this case, this context instance is at least the second one attempting to use |
| 276 | + * the static symbol cache. We now clear the static field to indicate that every |
| 277 | + * context instance should use its private cache. If a former context already used |
| 278 | + * the cache and there is already compiled code, it is not necessary to invalidate |
| 279 | + * the code because the cache is still valid. |
| 280 | + */ |
| 281 | + CApiContext.nativeSymbolCacheSingleContext = null; |
| 282 | + } |
| 283 | + CApiContext.nativeSymbolCacheSingleContextUsed = true; |
| 284 | + } |
242 | 285 |
|
243 | 286 | // initialize primitive native wrapper cache
|
244 | 287 | primitiveNativeWrapperCache = new PrimitiveNativeWrapper[262];
|
@@ -382,27 +425,59 @@ private void freeSmallInts() {
|
382 | 425 | }
|
383 | 426 | }
|
384 | 427 |
|
385 |
| - @TruffleBoundary |
386 |
| - @Override |
387 |
| - protected Store initializeSymbolCache() { |
388 |
| - PythonLanguage language = getContext().getLanguage(); |
389 |
| - Shape symbolCacheShape = language.getCApiSymbolCacheShape(); |
390 |
| - // We will always get an empty shape from the language and we do always add same key-value |
391 |
| - // pairs (in the same order). So, in the end, each context should get the same shape. |
392 |
| - Store s = new Store(symbolCacheShape); |
393 |
| - for (NativeCAPISymbol sym : NativeCAPISymbol.getValues()) { |
394 |
| - DynamicObjectLibrary.getUncached().put(s, sym, PNone.NO_VALUE); |
395 |
| - } |
396 |
| - return s; |
397 |
| - } |
398 |
| - |
399 | 428 | public Object getModuleByIndex(int i) {
|
400 | 429 | if (i < modulesByIndex.size()) {
|
401 | 430 | return modulesByIndex.get(i);
|
402 | 431 | }
|
403 | 432 | return null;
|
404 | 433 | }
|
405 | 434 |
|
| 435 | + /** |
| 436 | + * Retrieves the C API symbol cache instance in the fastest possible way. If there is just one |
| 437 | + * instance of {@link CApiContext}, it will load the cache stored from the static field |
| 438 | + * {@link CApiContext#nativeSymbolCacheSingleContext}. Otherwise, it will load the cache from |
| 439 | + * the instance field {@link CApiContext#nativeSymbolCache}. |
| 440 | + * |
| 441 | + * @param caller The requesting node (may be {@code null}). Used for the fast-path lookup of the |
| 442 | + * {@link CApiContext} instance (if necessary). |
| 443 | + * @return The C API symbol cache. |
| 444 | + */ |
| 445 | + private static Object[] getSymbolCache(Node caller) { |
| 446 | + Object[] nativeSymbolCacheSingleContext = CApiContext.nativeSymbolCacheSingleContext; |
| 447 | + if (nativeSymbolCacheSingleContext != null) { |
| 448 | + return nativeSymbolCacheSingleContext; |
| 449 | + } |
| 450 | + return PythonContext.get(caller).getCApiContext().nativeSymbolCache; |
| 451 | + } |
| 452 | + |
| 453 | + public static Object getNativeSymbol(Node caller, NativeCAPISymbol symbol) { |
| 454 | + Object[] nativeSymbolCache = getSymbolCache(caller); |
| 455 | + Object result = nativeSymbolCache[symbol.ordinal()]; |
| 456 | + if (result == null) { |
| 457 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 458 | + result = lookupNativeSymbol(nativeSymbolCache, symbol); |
| 459 | + } |
| 460 | + assert result != null; |
| 461 | + return result; |
| 462 | + } |
| 463 | + |
| 464 | + /** |
| 465 | + * Lookup the given C API symbol in the library, store it to the provided cache, and return the |
| 466 | + * callable symbol. |
| 467 | + */ |
| 468 | + private static Object lookupNativeSymbol(Object[] nativeSymbolCache, NativeCAPISymbol symbol) { |
| 469 | + CompilerAsserts.neverPartOfCompilation(); |
| 470 | + String name = symbol.getName(); |
| 471 | + try { |
| 472 | + Object nativeSymbol = InteropLibrary.getUncached().readMember(PythonContext.get(null).getCApiContext().getLLVMLibrary(), name); |
| 473 | + nativeSymbol = CExtContext.ensureExecutable(nativeSymbol, symbol); |
| 474 | + VarHandle.storeStoreFence(); |
| 475 | + return nativeSymbolCache[symbol.ordinal()] = nativeSymbol; |
| 476 | + } catch (UnsupportedMessageException | UnknownIdentifierException e) { |
| 477 | + throw CompilerDirectives.shouldNotReachHere(e); |
| 478 | + } |
| 479 | + } |
| 480 | + |
406 | 481 | @TruffleBoundary
|
407 | 482 | public AllocInfo traceFree(Object ptr, @SuppressWarnings("unused") PFrame.Reference curFrame, @SuppressWarnings("unused") TruffleString clazzName) {
|
408 | 483 | if (allocatedNativeMemory == null) {
|
@@ -746,6 +821,17 @@ public void finalizeCapi() {
|
746 | 821 | for (Object pyMethodDefPointer : methodDefinitions.values()) {
|
747 | 822 | PyMethodDefHelper.free(pyMethodDefPointer);
|
748 | 823 | }
|
| 824 | + /* |
| 825 | + * If the static symbol cache is not null, then it is guaranteed that this context instance |
| 826 | + * was the exclusive user of it. We can now reset the state such that other contexts created |
| 827 | + * after this can use it. |
| 828 | + */ |
| 829 | + synchronized (CApiContext.class) { |
| 830 | + if (CApiContext.nativeSymbolCacheSingleContext != null) { |
| 831 | + CApiContext.nativeSymbolCacheSingleContext = null; |
| 832 | + CApiContext.nativeSymbolCacheSingleContextUsed = false; |
| 833 | + } |
| 834 | + } |
749 | 835 | }
|
750 | 836 |
|
751 | 837 | @TruffleBoundary
|
|
0 commit comments