Skip to content

Commit

Permalink
fix: make Popover static methods thread-safe (#6864) (CP: 24.5) (#6868)
Browse files Browse the repository at this point in the history
Co-authored-by: Diego Cardoso <[email protected]>
  • Loading branch information
vaadin-bot and DiegoCardoso authored Nov 26, 2024
1 parent 571b67e commit afbb010
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import com.vaadin.flow.component.AttachEvent;
Expand Down Expand Up @@ -67,7 +68,8 @@ public class Popover extends Component implements HasAriaLabel, HasComponents,
private static Integer defaultHideDelay;
private static Integer defaultFocusDelay;
private static Integer defaultHoverDelay;
private static boolean uiInitListenerRegistered = false;
final static AtomicBoolean uiInitListenerRegistered = new AtomicBoolean(
false);

private Component target;
private Registration targetAttachRegistration;
Expand Down Expand Up @@ -148,11 +150,10 @@ private static void applyConfiguration() {
applyConfigurationForUI(UI.getCurrent());
}

if (!uiInitListenerRegistered) {
if (uiInitListenerRegistered.compareAndSet(false, true)) {
// Apply the popover configuration for all new UIs
VaadinService.getCurrent()
.addUIInitListener(e -> applyConfigurationForUI(e.getUI()));
uiInitListenerRegistered = true;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,22 @@
*/
package com.vaadin.flow.component.popover;

import static org.mockito.Mockito.times;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.server.VaadinService;

import elemental.json.JsonArray;

Expand Down Expand Up @@ -294,4 +304,48 @@ public void popoverWithContent() {
Assert.assertSame(content,
popoverWithContent.getChildren().findFirst().get());
}

@Test
public void testSetDefaultFocusDelay_threadSafety() {
testStaticSettersThreadsSafety(
() -> Popover.setDefaultFocusDelay(1000));
}

@Test
public void testSetDefaultHoverDelay_threadSafety() {
testStaticSettersThreadsSafety(
() -> Popover.setDefaultHoverDelay(1000));
}

@Test
public void testSetDefaultHideDelay_threadSafety() {
testStaticSettersThreadsSafety(() -> Popover.setDefaultHideDelay(1000));
}

private void testStaticSettersThreadsSafety(Runnable tester) {
// Reset the static state for each test
Popover.uiInitListenerRegistered.set(false);
final VaadinService current = Mockito.mock(VaadinService.class);

ExecutorService executorService = Executors.newFixedThreadPool(10);
final CountDownLatch latch = new CountDownLatch(10);
final Runnable runnable = () -> {
VaadinService.setCurrent(current);
latch.countDown();
for (int i = 0; i < 100000; i++) {
tester.run();
}
};
final var list = IntStream.range(0, 10)
.mapToObj(it -> executorService.submit(runnable)).toList();
for (var future : list) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
executorService.shutdown();
Mockito.verify(current, times(1)).addUIInitListener(Mockito.any());
}
}

0 comments on commit afbb010

Please sign in to comment.