Skip to content

Commit d2d0b57

Browse files
authored
Implement ForwardingTimeout.cancel() (#1395)
* Implement ForwardingTimeout.cancel() This needs lock-checking functions to be forwarded also, as the timeout now holds more state than before. * Functions are now open
1 parent bf29a91 commit d2d0b57

File tree

6 files changed

+83
-42
lines changed

6 files changed

+83
-42
lines changed

okio/api/okio.api

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,8 @@ public abstract class okio/ForwardingSource : okio/Source {
547547

548548
public class okio/ForwardingTimeout : okio/Timeout {
549549
public fun <init> (Lokio/Timeout;)V
550+
public fun awaitSignal (Ljava/util/concurrent/locks/Condition;)V
551+
public fun cancel ()V
550552
public fun clearDeadline ()Lokio/Timeout;
551553
public fun clearTimeout ()Lokio/Timeout;
552554
public fun deadlineNanoTime ()J
@@ -558,6 +560,7 @@ public class okio/ForwardingTimeout : okio/Timeout {
558560
public fun throwIfReached ()V
559561
public fun timeout (JLjava/util/concurrent/TimeUnit;)Lokio/Timeout;
560562
public fun timeoutNanos ()J
563+
public fun waitUntilNotified (Ljava/lang/Object;)V
561564
}
562565

563566
public final class okio/GzipSink : okio/Sink {
@@ -771,7 +774,7 @@ public class okio/Timeout {
771774
public static final field Companion Lokio/Timeout$Companion;
772775
public static final field NONE Lokio/Timeout;
773776
public fun <init> ()V
774-
public final fun awaitSignal (Ljava/util/concurrent/locks/Condition;)V
777+
public fun awaitSignal (Ljava/util/concurrent/locks/Condition;)V
775778
public fun cancel ()V
776779
public fun clearDeadline ()Lokio/Timeout;
777780
public fun clearTimeout ()Lokio/Timeout;
@@ -783,7 +786,7 @@ public class okio/Timeout {
783786
public fun throwIfReached ()V
784787
public fun timeout (JLjava/util/concurrent/TimeUnit;)Lokio/Timeout;
785788
public fun timeoutNanos ()J
786-
public final fun waitUntilNotified (Ljava/lang/Object;)V
789+
public fun waitUntilNotified (Ljava/lang/Object;)V
787790
}
788791

789792
public final class okio/Timeout$Companion {

okio/src/jvmMain/kotlin/okio/ForwardingTimeout.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package okio
1717

1818
import java.io.IOException
1919
import java.util.concurrent.TimeUnit
20+
import java.util.concurrent.locks.Condition
2021

2122
/** A [Timeout] which forwards calls to another. Useful for subclassing. */
2223
open class ForwardingTimeout(
@@ -49,4 +50,10 @@ open class ForwardingTimeout(
4950

5051
@Throws(IOException::class)
5152
override fun throwIfReached() = delegate.throwIfReached()
53+
54+
override fun cancel() = delegate.cancel()
55+
56+
override fun awaitSignal(condition: Condition) = delegate.awaitSignal(condition)
57+
58+
override fun waitUntilNotified(monitor: Any) = delegate.waitUntilNotified(monitor)
5259
}

okio/src/jvmMain/kotlin/okio/Timeout.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ actual open class Timeout {
171171
* ```
172172
*/
173173
@Throws(InterruptedIOException::class)
174-
fun awaitSignal(condition: Condition) {
174+
open fun awaitSignal(condition: Condition) {
175175
try {
176176
val hasDeadline = hasDeadline()
177177
val timeoutNanos = timeoutNanos()
@@ -248,7 +248,7 @@ actual open class Timeout {
248248
* ```
249249
*/
250250
@Throws(InterruptedIOException::class)
251-
fun waitUntilNotified(monitor: Any) {
251+
open fun waitUntilNotified(monitor: Any) {
252252
try {
253253
val hasDeadline = hasDeadline()
254254
val timeoutNanos = timeoutNanos()

okio/src/jvmTest/kotlin/okio/AwaitSignalTest.kt

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,15 @@ import org.junit.Assert.assertEquals
2525
import org.junit.Assert.assertTrue
2626
import org.junit.Assert.fail
2727
import org.junit.Test
28-
29-
class AwaitSignalTest {
28+
import org.junit.runner.RunWith
29+
import org.junit.runners.Parameterized
30+
import org.junit.runners.Parameterized.Parameters
31+
32+
@RunWith(Parameterized::class)
33+
class AwaitSignalTest(
34+
factory: TimeoutFactory,
35+
) {
36+
private val timeout = factory.newTimeout()
3037
val executorService = TestingExecutors.newScheduledExecutorService(0)
3138

3239
val lock: ReentrantLock = ReentrantLock()
@@ -39,7 +46,6 @@ class AwaitSignalTest {
3946

4047
@Test
4148
fun signaled() = lock.withLock {
42-
val timeout = Timeout()
4349
timeout.timeout(5000, TimeUnit.MILLISECONDS)
4450
val start = now()
4551
executorService.schedule(
@@ -54,7 +60,6 @@ class AwaitSignalTest {
5460
@Test
5561
fun timeout() = lock.withLock {
5662
assumeNotWindows()
57-
val timeout = Timeout()
5863
timeout.timeout(1000, TimeUnit.MILLISECONDS)
5964
val start = now()
6065
try {
@@ -69,7 +74,6 @@ class AwaitSignalTest {
6974
@Test
7075
fun deadline() = lock.withLock {
7176
assumeNotWindows()
72-
val timeout = Timeout()
7377
timeout.deadline(1000, TimeUnit.MILLISECONDS)
7478
val start = now()
7579
try {
@@ -84,7 +88,6 @@ class AwaitSignalTest {
8488
@Test
8589
fun deadlineBeforeTimeout() = lock.withLock {
8690
assumeNotWindows()
87-
val timeout = Timeout()
8891
timeout.timeout(5000, TimeUnit.MILLISECONDS)
8992
timeout.deadline(1000, TimeUnit.MILLISECONDS)
9093
val start = now()
@@ -100,7 +103,6 @@ class AwaitSignalTest {
100103
@Test
101104
fun timeoutBeforeDeadline() = lock.withLock {
102105
assumeNotWindows()
103-
val timeout = Timeout()
104106
timeout.timeout(1000, TimeUnit.MILLISECONDS)
105107
timeout.deadline(5000, TimeUnit.MILLISECONDS)
106108
val start = now()
@@ -116,7 +118,6 @@ class AwaitSignalTest {
116118
@Test
117119
fun deadlineAlreadyReached() = lock.withLock {
118120
assumeNotWindows()
119-
val timeout = Timeout()
120121
timeout.deadlineNanoTime(System.nanoTime())
121122
val start = now()
122123
try {
@@ -131,7 +132,6 @@ class AwaitSignalTest {
131132
@Test
132133
fun threadInterrupted() = lock.withLock {
133134
assumeNotWindows()
134-
val timeout = Timeout()
135135
val start = now()
136136
Thread.currentThread().interrupt()
137137
try {
@@ -147,7 +147,6 @@ class AwaitSignalTest {
147147
@Test
148148
fun threadInterruptedOnThrowIfReached() = lock.withLock {
149149
assumeNotWindows()
150-
val timeout = Timeout()
151150
Thread.currentThread().interrupt()
152151
try {
153152
timeout.throwIfReached()
@@ -159,15 +158,12 @@ class AwaitSignalTest {
159158
}
160159

161160
@Test
162-
fun cancelBeforeWaitDoesNothing() {
163-
val timeout = Timeout()
161+
fun cancelBeforeWaitDoesNothing() = lock.withLock {
164162
timeout.timeout(1000, TimeUnit.MILLISECONDS)
165163
timeout.cancel()
166164
val start = now()
167165
try {
168-
lock.withLock {
169-
timeout.awaitSignal(condition)
170-
}
166+
timeout.awaitSignal(condition)
171167
fail()
172168
} catch (expected: InterruptedIOException) {
173169
assertEquals("timeout", expected.message)
@@ -176,32 +172,26 @@ class AwaitSignalTest {
176172
}
177173

178174
@Test
179-
fun canceledTimeoutDoesNotThrowWhenNotNotifiedOnTime() {
175+
fun canceledTimeoutDoesNotThrowWhenNotNotifiedOnTime() = lock.withLock {
180176
assumeNotWindows()
181-
val timeout = Timeout()
182177
timeout.timeout(1000, TimeUnit.MILLISECONDS)
183178
timeout.cancelLater(500)
184179

185180
val start = now()
186-
lock.withLock {
187-
timeout.awaitSignal(condition) // Returns early but doesn't throw.
188-
}
181+
timeout.awaitSignal(condition) // Returns early but doesn't throw.
189182
assertElapsed(1000.0, start)
190183
}
191184

192185
@Test
193186
@Synchronized
194-
fun multipleCancelsAreIdempotent() {
195-
val timeout = Timeout()
187+
fun multipleCancelsAreIdempotent() = lock.withLock {
196188
timeout.timeout(1000, TimeUnit.MILLISECONDS)
197189
timeout.cancelLater(250)
198190
timeout.cancelLater(500)
199191
timeout.cancelLater(750)
200192

201193
val start = now()
202-
lock.withLock {
203-
timeout.awaitSignal(condition) // Returns early but doesn't throw.
204-
}
194+
timeout.awaitSignal(condition) // Returns early but doesn't throw.
205195
assertElapsed(1000.0, start)
206196
}
207197

@@ -225,4 +215,10 @@ class AwaitSignalTest {
225215
TimeUnit.MILLISECONDS,
226216
)
227217
}
218+
219+
companion object {
220+
@Parameters(name = "{0}")
221+
@JvmStatic
222+
fun parameters(): List<Array<out Any?>> = TimeoutFactory.entries.map { arrayOf(it) }
223+
}
228224
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (C) 2023 Square, Inc.
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+
package okio
17+
18+
enum class TimeoutFactory {
19+
BASE {
20+
override fun newTimeout() = Timeout()
21+
},
22+
23+
FORWARDING {
24+
override fun newTimeout() = ForwardingTimeout(BASE.newTimeout())
25+
},
26+
27+
ASYNC {
28+
override fun newTimeout() = AsyncTimeout()
29+
},
30+
;
31+
32+
abstract fun newTimeout(): Timeout
33+
}

okio/src/jvmTest/kotlin/okio/WaitUntilNotifiedTest.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,15 @@ import org.junit.Assert.assertEquals
2424
import org.junit.Assert.assertTrue
2525
import org.junit.Assert.fail
2626
import org.junit.Test
27-
28-
class WaitUntilNotifiedTest {
27+
import org.junit.runner.RunWith
28+
import org.junit.runners.Parameterized
29+
import org.junit.runners.Parameterized.Parameters
30+
31+
@RunWith(Parameterized::class)
32+
class WaitUntilNotifiedTest(
33+
factory: TimeoutFactory,
34+
) {
35+
private val timeout = factory.newTimeout()
2936
private val executorService = newScheduledExecutorService(0)
3037

3138
@After
@@ -36,7 +43,6 @@ class WaitUntilNotifiedTest {
3643
@Test
3744
@Synchronized
3845
fun notified() {
39-
val timeout = Timeout()
4046
timeout.timeout(5000, TimeUnit.MILLISECONDS)
4147
val start = now()
4248
executorService.schedule(
@@ -56,7 +62,6 @@ class WaitUntilNotifiedTest {
5662
@Synchronized
5763
fun timeout() {
5864
assumeNotWindows()
59-
val timeout = Timeout()
6065
timeout.timeout(1000, TimeUnit.MILLISECONDS)
6166
val start = now()
6267
try {
@@ -72,7 +77,6 @@ class WaitUntilNotifiedTest {
7277
@Synchronized
7378
fun deadline() {
7479
assumeNotWindows()
75-
val timeout = Timeout()
7680
timeout.deadline(1000, TimeUnit.MILLISECONDS)
7781
val start = now()
7882
try {
@@ -88,7 +92,6 @@ class WaitUntilNotifiedTest {
8892
@Synchronized
8993
fun deadlineBeforeTimeout() {
9094
assumeNotWindows()
91-
val timeout = Timeout()
9295
timeout.timeout(5000, TimeUnit.MILLISECONDS)
9396
timeout.deadline(1000, TimeUnit.MILLISECONDS)
9497
val start = now()
@@ -105,7 +108,6 @@ class WaitUntilNotifiedTest {
105108
@Synchronized
106109
fun timeoutBeforeDeadline() {
107110
assumeNotWindows()
108-
val timeout = Timeout()
109111
timeout.timeout(1000, TimeUnit.MILLISECONDS)
110112
timeout.deadline(5000, TimeUnit.MILLISECONDS)
111113
val start = now()
@@ -122,7 +124,6 @@ class WaitUntilNotifiedTest {
122124
@Synchronized
123125
fun deadlineAlreadyReached() {
124126
assumeNotWindows()
125-
val timeout = Timeout()
126127
timeout.deadlineNanoTime(System.nanoTime())
127128
val start = now()
128129
try {
@@ -138,7 +139,6 @@ class WaitUntilNotifiedTest {
138139
@Synchronized
139140
fun threadInterrupted() {
140141
assumeNotWindows()
141-
val timeout = Timeout()
142142
val start = now()
143143
Thread.currentThread().interrupt()
144144
try {
@@ -155,7 +155,6 @@ class WaitUntilNotifiedTest {
155155
@Synchronized
156156
fun threadInterruptedOnThrowIfReached() {
157157
assumeNotWindows()
158-
val timeout = Timeout()
159158
Thread.currentThread().interrupt()
160159
try {
161160
timeout.throwIfReached()
@@ -170,7 +169,6 @@ class WaitUntilNotifiedTest {
170169
@Synchronized
171170
fun cancelBeforeWaitDoesNothing() {
172171
assumeNotWindows()
173-
val timeout = Timeout()
174172
timeout.timeout(1000, TimeUnit.MILLISECONDS)
175173
timeout.cancel()
176174
val start = now()
@@ -186,7 +184,6 @@ class WaitUntilNotifiedTest {
186184
@Test
187185
@Synchronized
188186
fun canceledTimeoutDoesNotThrowWhenNotNotifiedOnTime() {
189-
val timeout = Timeout()
190187
timeout.timeout(1000, TimeUnit.MILLISECONDS)
191188
timeout.cancelLater(500)
192189

@@ -198,7 +195,6 @@ class WaitUntilNotifiedTest {
198195
@Test
199196
@Synchronized
200197
fun multipleCancelsAreIdempotent() {
201-
val timeout = Timeout()
202198
timeout.timeout(1000, TimeUnit.MILLISECONDS)
203199
timeout.cancelLater(250)
204200
timeout.cancelLater(500)
@@ -231,4 +227,10 @@ class WaitUntilNotifiedTest {
231227
TimeUnit.MILLISECONDS,
232228
)
233229
}
230+
231+
companion object {
232+
@Parameters(name = "{0}")
233+
@JvmStatic
234+
fun parameters(): List<Array<out Any?>> = TimeoutFactory.entries.map { arrayOf(it) }
235+
}
234236
}

0 commit comments

Comments
 (0)