@@ -680,51 +680,64 @@ void configurationError_ClosesResources() {
680680 }
681681
682682 @ Test
683- @ SuppressWarnings ({"unchecked" , "FutureReturnValueIgnored" })
684- void test () throws Exception {
685- // Idea of the test:
686- // 1. Thread 1 calls AutoConfiguredOpenTelemetrySdkBuilder#build
687- // 2. It acquires the mutex in GlobalOpenTelemetry.set(Supplier<OpenTelemetry>)
688- // 3. While holding the mutex before the call to GlobalOpenTelemetry.set(OpenTelemetry),
689- // it spawns Thread 2 which calls GlobalOpenTelemetry.get
690- // 4. We test that Thread 2 does not fail, and that its result is the same OpenTelemetry
691- // instance embedded in the AutoConfiguredOpenTelemetrySdk
692- SynchronousQueue <OpenTelemetry > gotGetResult = new SynchronousQueue <>();
693-
694- try (MockedStatic <GlobalOpenTelemetry > mockGot =
695- Mockito .mockStatic (GlobalOpenTelemetry .class )) {
696- mockGot .when (GlobalOpenTelemetry ::get ).thenCallRealMethod ();
697- mockGot .when (() -> GlobalOpenTelemetry .set (any (Supplier .class ))).thenCallRealMethod ();
698- mockGot
699- .when (() -> GlobalOpenTelemetry .set (any (OpenTelemetry .class )))
700- .then (
701- invocation -> {
702- CompletableFuture .supplyAsync (GlobalOpenTelemetry ::get )
703- .handle (
704- (sdk , exc ) -> {
705- if (exc != null ) {
706- Assertions .fail (exc );
707- } else {
708- try {
709- gotGetResult .put (sdk );
710- } catch (InterruptedException exception ) {
711- Thread .currentThread ().interrupt ();
712- Assertions .fail (exception );
713- }
683+ void globalOpenTelemetryLock () throws InterruptedException , ExecutionException , TimeoutException {
684+ CountDownLatch autoconfigStarted = new CountDownLatch (1 );
685+ CountDownLatch completeAutoconfig = new CountDownLatch (1 );
686+ ExecutorService executorService = Executors .newFixedThreadPool (2 );
687+
688+ // Submit a future to autoconfigure the SDK and set the result as global. Add a customization
689+ // hook which blocks until we say so.
690+ CompletableFuture <OpenTelemetrySdk > autoConfiguredOpenTelemetryFuture =
691+ CompletableFuture .supplyAsync (
692+ () ->
693+ builder
694+ .addLoggerProviderCustomizer (
695+ (sdkLoggerProviderBuilder , configProperties ) -> {
696+ autoconfigStarted .countDown ();
697+ try {
698+ completeAutoconfig .await ();
699+ } catch (InterruptedException e ) {
700+ Thread .currentThread ().interrupt ();
714701 }
715- return null ;
716- });
717- // Give Thread 2 some time to try obtaining the mutex and getting blocked
718- Thread .sleep (500 );
719- return invocation .callRealMethod ();
720- });
721-
722- AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
723- builder .setResultAsGlobal ().build ();
724-
725- assertThat (gotGetResult .poll (3 , TimeUnit .SECONDS ))
726- .extracting ("delegate" )
727- .isSameAs (autoConfiguredOpenTelemetrySdk .getOpenTelemetrySdk ());
702+ return sdkLoggerProviderBuilder ;
703+ })
704+ .setResultAsGlobal ()
705+ .build ()
706+ .getOpenTelemetrySdk (),
707+ executorService );
708+
709+ // Wait for autoconfiguration to enter our callback, then try to get an instance of
710+ // GlobalOpenTelemetry. GlobalOpenTelemetry.get() should block until we release the
711+ // completeAutoconfig latch and allow autoconfiguration to complete.
712+ autoconfigStarted .await ();
713+ CompletableFuture <OpenTelemetry > globalOpenTelemetryFuture =
714+ CompletableFuture .supplyAsync (GlobalOpenTelemetry ::get , executorService );
715+ Thread .sleep (10 );
716+ assertThat (globalOpenTelemetryFuture .isDone ()).isFalse ();
717+ assertThat (autoConfiguredOpenTelemetryFuture .isDone ()).isFalse ();
718+
719+ // Release the latch, allowing autoconfiguration to complete. Confirm that our
720+ // GlobalOpenTelemetry.get() future resolved to the same instance as autoconfiguration.
721+ completeAutoconfig .countDown ();
722+ assertThat (unobfuscate (globalOpenTelemetryFuture .get (10 , TimeUnit .SECONDS )))
723+ .isSameAs (autoConfiguredOpenTelemetryFuture .get (10 , TimeUnit .SECONDS ));
724+
725+ // Cleanup
726+ executorService .shutdown ();
727+ autoConfiguredOpenTelemetryFuture .get ().shutdown ().join (10 , TimeUnit .SECONDS );
728+ GlobalOpenTelemetry .resetForTest ();
729+ }
730+
731+ private static OpenTelemetry unobfuscate (OpenTelemetry openTelemetry ) {
732+ try {
733+ Field delegateField =
734+ Class .forName ("io.opentelemetry.api.GlobalOpenTelemetry$ObfuscatedOpenTelemetry" )
735+ .getDeclaredField ("delegate" );
736+ delegateField .setAccessible (true );
737+ Object delegate = delegateField .get (openTelemetry );
738+ return (OpenTelemetry ) delegate ;
739+ } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e ) {
740+ throw new IllegalStateException ("Error unobfuscating OpenTelemetry" , e );
728741 }
729742 }
730743}
0 commit comments