Skip to content

Commit 48240da

Browse files
author
Andy Goryachev
committed
8088343: Race condition in javafx.concurrent.Task::cancel
Reviewed-by: kcr, arapte
1 parent 22064a8 commit 48240da

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

modules/javafx.graphics/src/main/java/javafx/concurrent/Task.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,14 @@
2525

2626
package javafx.concurrent;
2727

28+
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_CANCELLED;
29+
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_FAILED;
30+
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_RUNNING;
31+
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SCHEDULED;
32+
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SUCCEEDED;
33+
import java.util.concurrent.Callable;
34+
import java.util.concurrent.FutureTask;
35+
import java.util.concurrent.atomic.AtomicReference;
2836
import javafx.application.Platform;
2937
import javafx.beans.property.BooleanProperty;
3038
import javafx.beans.property.DoubleProperty;
@@ -43,14 +51,6 @@
4351
import javafx.event.EventHandler;
4452
import javafx.event.EventTarget;
4553
import javafx.event.EventType;
46-
import java.util.concurrent.Callable;
47-
import java.util.concurrent.FutureTask;
48-
import java.util.concurrent.atomic.AtomicReference;
49-
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_CANCELLED;
50-
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_FAILED;
51-
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_RUNNING;
52-
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SCHEDULED;
53-
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SUCCEEDED;
5454

5555
/**
5656
* <p>
@@ -998,7 +998,8 @@ protected void failed() { }
998998
return cancel(true);
999999
}
10001000

1001-
@Override public boolean cancel(boolean mayInterruptIfRunning) {
1001+
@Override
1002+
public boolean cancel(boolean mayInterruptIfRunning) {
10021003
// Delegate to the super implementation to actually attempt to cancel this thing
10031004
// Assert the modifyThread permission
10041005
boolean flag = super.cancel(mayInterruptIfRunning);
@@ -1014,12 +1015,30 @@ protected void failed() { }
10141015
// state flag will not be readable immediately after this call. However,
10151016
// that would be the case anyway since these properties are not thread-safe.
10161017
if (isFxApplicationThread()) {
1018+
switch (getState()) {
1019+
case FAILED:
1020+
case SUCCEEDED:
1021+
// a finished or failed task retains its state
1022+
return false;
1023+
}
1024+
10171025
setState(Worker.State.CANCELLED);
10181026
} else {
1019-
runLater(() -> setState(Worker.State.CANCELLED));
1027+
runLater(() -> {
1028+
// the state must be accessed only in the fx application thread
1029+
switch (getState()) {
1030+
case FAILED:
1031+
case SUCCEEDED:
1032+
// a finished or failed task retains its state
1033+
break;
1034+
default:
1035+
setState(Worker.State.CANCELLED);
1036+
break;
1037+
}
1038+
});
1039+
return flag;
10201040
}
10211041
}
1022-
// return the flag
10231042
return flag;
10241043
}
10251044

modules/javafx.graphics/src/test/java/test/javafx/concurrent/ServiceLifecycleTest.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,13 @@
2525

2626
package test.javafx.concurrent;
2727

28-
import test.javafx.concurrent.mocks.MythicalEvent;
29-
import test.javafx.concurrent.mocks.SimpleTask;
30-
import javafx.event.EventHandler;
28+
import static org.junit.jupiter.api.Assertions.assertEquals;
29+
import static org.junit.jupiter.api.Assertions.assertFalse;
30+
import static org.junit.jupiter.api.Assertions.assertNotNull;
31+
import static org.junit.jupiter.api.Assertions.assertNull;
32+
import static org.junit.jupiter.api.Assertions.assertSame;
33+
import static org.junit.jupiter.api.Assertions.assertThrows;
34+
import static org.junit.jupiter.api.Assertions.assertTrue;
3135
import java.util.concurrent.ConcurrentLinkedQueue;
3236
import java.util.concurrent.CountDownLatch;
3337
import java.util.concurrent.Executor;
@@ -40,17 +44,13 @@
4044
import javafx.concurrent.TaskShim;
4145
import javafx.concurrent.Worker;
4246
import javafx.concurrent.WorkerStateEvent;
43-
47+
import javafx.event.EventHandler;
4448
import org.junit.jupiter.api.AfterEach;
4549
import org.junit.jupiter.api.BeforeEach;
50+
import org.junit.jupiter.api.RepeatedTest;
4651
import org.junit.jupiter.api.Test;
47-
import static org.junit.jupiter.api.Assertions.assertEquals;
48-
import static org.junit.jupiter.api.Assertions.assertFalse;
49-
import static org.junit.jupiter.api.Assertions.assertNotNull;
50-
import static org.junit.jupiter.api.Assertions.assertNull;
51-
import static org.junit.jupiter.api.Assertions.assertSame;
52-
import static org.junit.jupiter.api.Assertions.assertThrows;
53-
import static org.junit.jupiter.api.Assertions.assertTrue;
52+
import test.javafx.concurrent.mocks.MythicalEvent;
53+
import test.javafx.concurrent.mocks.SimpleTask;
5454

5555
/**
5656
* Tests various rules regarding the lifecycle of a Service.
@@ -1278,6 +1278,7 @@ public void removed_onSucceededFilterNotCalled() {
12781278
task.complete();
12791279
}
12801280

1281+
@RepeatedTest(50)
12811282
@Test
12821283
public void cancelCalledFromOnSucceeded() {
12831284
final AtomicInteger cancelNotificationCount = new AtomicInteger();
@@ -1422,6 +1423,7 @@ public void cancelCalledFromOnCancelled() {
14221423
assertEquals(1, cancelNotificationCount.get());
14231424
}
14241425

1426+
@RepeatedTest(50)
14251427
@Test
14261428
public void cancelCalledFromOnFailed() {
14271429
final AtomicInteger cancelNotificationCount = new AtomicInteger();

0 commit comments

Comments
 (0)