1 /*
<lambda>null2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 @file:JvmName("ConcurrentUtils")
18
19 package com.android.testutils
20
21 import java.util.concurrent.CountDownLatch
22 import java.util.concurrent.ExecutorService
23 import java.util.concurrent.TimeUnit
24 import java.util.function.Consumer
25 import kotlin.system.measureTimeMillis
26 import kotlin.test.assertFalse
27 import kotlin.test.assertTrue
28
29 // For Java usage
30 fun durationOf(fn: Runnable) = measureTimeMillis { fn.run() }
31
awaitnull32 fun CountDownLatch.await(timeoutMs: Long): Boolean = await(timeoutMs, TimeUnit.MILLISECONDS)
33
34 /**
35 * Quit resources provided as a list by a supplier.
36 *
37 * The supplier may return more resources as the process progresses, for example while interrupting
38 * threads and waiting for them to finish they may spawn more threads, so this implements a
39 * [maxRetryCount] which, in this case, would be the maximum length of the thread chain that can be
40 * terminated.
41 */
42 fun <T> quitResources(
43 maxRetryCount: Int,
44 supplier: () -> List<T>,
45 terminator: Consumer<T>
46 ) {
47 // Run it multiple times since new threads might be generated in a thread
48 // that is about to be terminated
49 for (retryCount in 0 until maxRetryCount) {
50 val resourcesToBeCleared = supplier()
51 if (resourcesToBeCleared.isEmpty()) return
52 for (resource in resourcesToBeCleared) {
53 terminator.accept(resource)
54 }
55 }
56 assertEmpty(supplier())
57 }
58
59 /**
60 * Implementation of [quitResources] to interrupt and wait for [ExecutorService]s to finish.
61 */
62 @JvmOverloads
quitExecutorServicesnull63 fun quitExecutorServices(
64 maxRetryCount: Int,
65 interrupt: Boolean = true,
66 timeoutMs: Long = 10_000L,
67 supplier: () -> List<ExecutorService>
68 ) {
69 quitResources(maxRetryCount, supplier) { ecs ->
70 if (interrupt) {
71 ecs.shutdownNow()
72 }
73 assertTrue(ecs.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS),
74 "ExecutorServices did not terminate within timeout")
75 }
76 }
77
78 /**
79 * Implementation of [quitResources] to interrupt and wait for [Thread]s to finish.
80 */
81 @JvmOverloads
quitThreadsnull82 fun quitThreads(
83 maxRetryCount: Int,
84 interrupt: Boolean = true,
85 timeoutMs: Long = 10_000L,
86 supplier: () -> List<Thread>
87 ) {
88 quitResources(maxRetryCount, supplier) { th ->
89 if (interrupt) {
90 th.interrupt()
91 }
92 th.join(timeoutMs)
93 assertFalse(th.isAlive, "Threads did not terminate within timeout.")
94 }
95 }
96