Skip to content

Commit 8d2d745

Browse files
committed
feat: Add close method on ActorSystem (apache#2486)
(cherry picked from commit d5ee8a6)
1 parent c68e089 commit 8d2d745

File tree

38 files changed

+224
-103
lines changed

38 files changed

+224
-103
lines changed

actor-testkit-typed/src/main/scala/org/apache/pekko/actor/testkit/typed/internal/ActorSystemStub.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ import pekko.util.FutureConverters._
109109
override def terminate(): Unit = terminationPromise.trySuccess(Done)
110110
override def whenTerminated: Future[Done] = terminationPromise.future
111111
override def getWhenTerminated: CompletionStage[Done] = whenTerminated.asJava
112+
override def close(): Unit = {
113+
terminate()
114+
Await.result(whenTerminated, scala.concurrent.duration.Duration.Inf)
115+
}
112116
override val startTime: Long = System.currentTimeMillis()
113117
override def uptime: Long = System.currentTimeMillis() - startTime
114118
override def threadFactory: java.util.concurrent.ThreadFactory = new ThreadFactory {

actor-tests/src/test/java/org/apache/pekko/actor/ActorSystemTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.apache.pekko.actor;
1515

16+
import static org.junit.Assert.assertTrue;
17+
1618
import org.apache.pekko.testkit.PekkoJUnitActorSystemResource;
1719
import org.junit.Before;
1820
import org.junit.Rule;
@@ -48,4 +50,14 @@ public void testGetWhenTerminated() throws Exception {
4850
public void testGetWhenTerminatedWithoutTermination() {
4951
assertFalse(system.getWhenTerminated().toCompletableFuture().isDone());
5052
}
53+
54+
@Test
55+
public void testTryWithResources() throws Exception {
56+
ActorSystem system = null;
57+
try (ActorSystem actorSystem = ActorSystem.create()) {
58+
system = actorSystem;
59+
}
60+
final CompletionStage<Terminated> cs = system.getWhenTerminated();
61+
assertTrue(cs.toCompletableFuture().isDone());
62+
}
5163
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.pekko.actor
19+
20+
import scala.util.Using
21+
22+
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
23+
24+
import org.apache.pekko.testkit.{ ImplicitSender, PekkoSpec }
25+
26+
class ActorSystemScala213PlusSpec extends PekkoSpec(ActorSystemSpec.config) with ImplicitSender
27+
with ScalaCheckPropertyChecks {
28+
"A Scala 2.13+ ActorSystem" must {
29+
"Scala's Using automatically terminates ActorSystem" in {
30+
var currentSystem: ActorSystem = null
31+
Using(ActorSystem()) { system =>
32+
currentSystem = system
33+
}
34+
currentSystem.whenTerminated.isCompleted should ===(true)
35+
}
36+
}
37+
38+
}

actor-tests/src/test/scala/org/apache/pekko/actor/ActorSystemSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class ActorSystemSpec extends PekkoSpec(ActorSystemSpec.config) with ImplicitSen
262262

263263
"throw RejectedExecutionException when shutdown" in {
264264
val system2 = ActorSystem("RejectedExecution-1", PekkoSpec.testConf)
265-
Await.ready(system2.terminate(), 10.seconds)
265+
system2.close()
266266

267267
intercept[RejectedExecutionException] {
268268
system2.registerOnTermination { println("IF YOU SEE THIS THEN THERE'S A BUG HERE") }

actor-tests/src/test/scala/org/apache/pekko/dispatch/DispatcherShutdownSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class DispatcherShutdownSpec extends AnyWordSpec with Matchers {
4545
val system = ActorSystem("DispatcherShutdownSpec")
4646
threadCount should be > 0
4747

48-
Await.ready(system.terminate(), 1.second)
48+
system.close()
4949
Await.ready(Future(pekko.Done)(system.dispatcher), 1.second)
5050

5151
TestKit.awaitCond(threadCount == 0, 3.second)

actor-typed-tests/src/test/java/org/apache/pekko/actor/typed/ActorSystemTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.apache.pekko.actor.typed;
1515

16+
import static org.junit.Assert.assertTrue;
17+
1618
import org.apache.pekko.Done;
1719
import org.apache.pekko.actor.typed.javadsl.Behaviors;
1820
import org.junit.Test;
@@ -40,4 +42,15 @@ public void testGetWhenTerminatedWithoutTermination() {
4042
ActorSystem.create(Behaviors.empty(), "GetWhenTerminatedWithoutTermination");
4143
assertFalse(system.getWhenTerminated().toCompletableFuture().isDone());
4244
}
45+
46+
@Test
47+
public void testTryWithResources() throws Exception {
48+
ActorSystem<Void> system = null;
49+
try (ActorSystem<Void> actorSystem =
50+
ActorSystem.create(Behaviors.empty(), "TryWithResourcesSystem")) {
51+
system = actorSystem;
52+
}
53+
final CompletionStage<Done> cs = system.getWhenTerminated();
54+
assertTrue(cs.toCompletableFuture().isDone());
55+
}
4356
}

actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/ActorSystemSpec.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.apache.pekko.actor.typed
1919

20+
import scala.util.Using
21+
2022
import com.typesafe.config.ConfigFactory
2123
import org.apache.pekko
2224
import pekko.actor.typed.scaladsl.Behaviors
@@ -44,5 +46,19 @@ class ActorSystemSpec extends PekkoSpec {
4446
system.terminate()
4547
}
4648
}
49+
50+
"close method terminates ActorSystem" in {
51+
val system = ActorSystem(Behaviors.empty[String], "close-terminates-system")
52+
system.close()
53+
system.whenTerminated.isCompleted should ===(true)
54+
}
55+
56+
"Scala's Using automatically terminates ActorSystem" in {
57+
var currentSystem: ActorSystem[Nothing] = null
58+
Using(ActorSystem(Behaviors.empty[String], "using-terminates-system")) { system =>
59+
currentSystem = system
60+
}
61+
currentSystem.whenTerminated.isCompleted should ===(true)
62+
}
4763
}
4864
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# Add close to ActorSystem
19+
ProblemFilters.exclude[ReversedMissingMethodProblem]("org.apache.pekko.actor.typed.ActorSystem.close")
20+
ProblemFilters.exclude[InheritedNewAbstractMethodProblem]("org.apache.pekko.actor.typed.ActorSystem.close")

actor-typed/src/main/scala/org/apache/pekko/actor/typed/ActorSystem.scala

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
package org.apache.pekko.actor.typed
1515

16-
import java.util.concurrent.{ CompletionStage, ThreadFactory }
16+
import java.util.concurrent.{ CompletionStage, ThreadFactory, TimeoutException }
1717

1818
import scala.concurrent.{ ExecutionContextExecutor, Future }
1919

@@ -40,7 +40,7 @@ import pekko.util.Helpers.{ ConfigOps, Requiring }
4040
* Not for user extension.
4141
*/
4242
@DoNotInherit
43-
abstract class ActorSystem[-T] extends ActorRef[T] with Extensions with ClassicActorSystemProvider {
43+
abstract class ActorSystem[-T] extends ActorRef[T] with Extensions with ClassicActorSystemProvider with AutoCloseable {
4444
this: InternalRecipientRef[T] =>
4545

4646
/**
@@ -145,6 +145,26 @@ abstract class ActorSystem[-T] extends ActorRef[T] with Extensions with ClassicA
145145
*/
146146
def getWhenTerminated: CompletionStage[Done]
147147

148+
/**
149+
* Terminates this actor system by running [[pekko.actor.CoordinatedShutdown]] with reason
150+
* [[pekko.actor.CoordinatedShutdown.ActorSystemTerminateReason]]. This method will block
151+
* until either the actor system is terminated or
152+
* `pekko.coordinated-shutdown.close-actor-system-timeout` timeout duration is
153+
* passed, in which case a [[TimeoutException]] is thrown.
154+
*
155+
* If `pekko.coordinated-shutdown.run-by-actor-system-terminate` is configured to `off`
156+
* it will not run `CoordinatedShutdown`, but the `ActorSystem` and its actors
157+
* will still be terminated.
158+
*
159+
* This will stop the guardian actor, which in turn
160+
* will recursively stop all its child actors, and finally the system guardian
161+
* (below which the logging actors reside) and then execute all registered
162+
* termination handlers (see [[pekko.actor.ActorSystem.registerOnTermination]]).
163+
* @since 1.3.0
164+
*/
165+
@throws(classOf[TimeoutException])
166+
override def close(): Unit
167+
148168
/**
149169
* The deadLetter address is a destination that will accept (and discard)
150170
* every message sent to it.

actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorSystemAdapter.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ import pekko.util.FutureConverters._
122122
override lazy val getWhenTerminated: CompletionStage[pekko.Done] =
123123
whenTerminated.asJava
124124

125+
override def close(): Unit = system.close()
126+
125127
override def systemActorOf[U](behavior: Behavior[U], name: String, props: Props): ActorRef[U] = {
126128
val ref = system.systemActorOf(
127129
PropsAdapter(

0 commit comments

Comments
 (0)