FixedThreadPoolBulkhead implements ThreadPoolBulkhead { @Override public <T> CompletableFuture<T> submit(Callable<T> callable) { final CompletableFuture<T> promise = new CompletableFuture<>(); try { CompletableFuture.supplyAsync(ContextPropagator.decorateSupplier(config.getContextPropagator(),() -> { try { publishBulkheadEvent(() -> new BulkheadOnCallPermittedEvent(name)); return callable.call(); } catch (CompletionException e) { throw e; } catch (Exception e){ throw new CompletionException(e); } }), executorService).whenComplete((result, throwable) -> { publishBulkheadEvent(() -> new BulkheadOnCallFinishedEvent(name)); if (throwable != null) { promise.completeExceptionally(throwable); } else { promise.complete(result); } }); } catch (RejectedExecutionException rejected) { publishBulkheadEvent(() -> new BulkheadOnCallRejectedEvent(name)); throw BulkheadFullException.createBulkheadFullException(this); } return promise; } FixedThreadPoolBulkhead(String name, @Nullable ThreadPoolBulkheadConfig bulkheadConfig);  FixedThreadPoolBulkhead(String name, @Nullable ThreadPoolBulkheadConfig bulkheadConfig,
        Map<String, String> tags);  FixedThreadPoolBulkhead(String name);  FixedThreadPoolBulkhead(String name, Map<String, String> tags);  FixedThreadPoolBulkhead(String name, Supplier<ThreadPoolBulkheadConfig> configSupplier);  FixedThreadPoolBulkhead(String name, Supplier<ThreadPoolBulkheadConfig> configSupplier,
        Map<String, String> tags); @Override CompletableFuture<T> submit(Callable<T> callable); @Override CompletableFuture<Void> submit(Runnable runnable); @Override String getName(); @Override ThreadPoolBulkheadConfig getBulkheadConfig(); @Override Metrics getMetrics(); @Override Map<String, String> getTags(); @Override ThreadPoolBulkheadEventPublisher getEventPublisher(); @Override String toString(); @Override void close();  }
@Test public void testSupplierThreadLocalContextPropagator() { TestThreadLocalContextHolder.put("ValueShouldCrossThreadBoundary"); CompletableFuture<Object> future = fixedThreadPoolBulkhead .submit(() -> TestThreadLocalContextHolder.get().orElse(null)); waitAtMost(5, TimeUnit.SECONDS).until(matches(() -> assertThat(future).isCompletedWithValue("ValueShouldCrossThreadBoundary"))); }
@Test public void testRunnableThreadLocalContextPropagator() { TestThreadLocalContextHolder.put("ValueShouldCrossThreadBoundary"); AtomicReference<String> reference = new AtomicReference<>(); fixedThreadPoolBulkhead .submit(() -> reference.set((String) TestThreadLocalContextHolder.get().orElse(null))); waitAtMost(5, TimeUnit.SECONDS).until(matches(() -> assertThat(reference).hasValue("ValueShouldCrossThreadBoundary"))); }