Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 1 | /* |
Roman Elizarov | 1f74a2d | 2018-06-29 19:19:45 +0300 | [diff] [blame] | 2 | * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 3 | */ |
| 4 | |
Roman Elizarov | 0950dfa | 2018-07-13 10:33:25 +0300 | [diff] [blame] | 5 | package kotlinx.coroutines |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 6 | |
Roman Elizarov | c0d71dc | 2017-12-21 22:12:43 +0300 | [diff] [blame] | 7 | import kotlin.test.* |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 8 | import java.util.concurrent.ExecutorService |
| 9 | import java.util.concurrent.Executors |
| 10 | import java.util.concurrent.ThreadFactory |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 11 | import java.util.concurrent.atomic.AtomicInteger |
Roman Elizarov | 0950dfa | 2018-07-13 10:33:25 +0300 | [diff] [blame] | 12 | import kotlin.coroutines.CoroutineContext |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 13 | |
| 14 | class WithTimeoutOrNullThreadDispatchTest : TestBase() { |
| 15 | var executor: ExecutorService? = null |
| 16 | |
Roman Elizarov | c0d71dc | 2017-12-21 22:12:43 +0300 | [diff] [blame] | 17 | @AfterTest |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 18 | fun tearDown() { |
| 19 | executor?.shutdown() |
| 20 | } |
| 21 | |
| 22 | @Test |
| 23 | fun testCancellationDispatchScheduled() { |
| 24 | checkCancellationDispatch { |
| 25 | executor = Executors.newScheduledThreadPool(1, it) |
| 26 | executor!!.asCoroutineDispatcher() |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | @Test |
| 31 | fun testCancellationDispatchNonScheduled() { |
| 32 | checkCancellationDispatch { |
| 33 | executor = Executors.newSingleThreadExecutor(it) |
| 34 | executor!!.asCoroutineDispatcher() |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | @Test |
| 39 | fun testCancellationDispatchCustomNoDelay() { |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 40 | // it also checks that there is at most once scheduled request in flight (no spurious concurrency) |
| 41 | var error: String? = null |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 42 | checkCancellationDispatch { |
| 43 | executor = Executors.newSingleThreadExecutor(it) |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 44 | val scheduled = AtomicInteger(0) |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 45 | object : CoroutineDispatcher() { |
| 46 | override fun dispatch(context: CoroutineContext, block: Runnable) { |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 47 | if (scheduled.incrementAndGet() > 1) error = "Two requests are scheduled concurrently" |
| 48 | executor!!.execute { |
| 49 | scheduled.decrementAndGet() |
| 50 | block.run() |
| 51 | } |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 52 | } |
| 53 | } |
| 54 | } |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 55 | error?.let { error(it) } |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | private fun checkCancellationDispatch(factory: (ThreadFactory) -> CoroutineDispatcher) = runBlocking { |
| 59 | expect(1) |
| 60 | var thread: Thread? = null |
| 61 | val dispatcher = factory(ThreadFactory { Thread(it).also { thread = it } }) |
Roman Elizarov | f9e13f5 | 2017-12-21 12:23:15 +0300 | [diff] [blame] | 62 | withContext(dispatcher) { |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 63 | expect(2) |
Roman Elizarov | c0d71dc | 2017-12-21 22:12:43 +0300 | [diff] [blame] | 64 | assertEquals(thread, Thread.currentThread()) |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 65 | val result = withTimeoutOrNull(100) { |
| 66 | try { |
| 67 | expect(3) |
| 68 | delay(1000) |
| 69 | expectUnreached() |
| 70 | } catch (e: CancellationException) { |
| 71 | expect(4) |
Roman Elizarov | c0d71dc | 2017-12-21 22:12:43 +0300 | [diff] [blame] | 72 | assertEquals(thread, Thread.currentThread()) |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 73 | throw e // rethrow |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 74 | } |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 75 | } |
Roman Elizarov | c0d71dc | 2017-12-21 22:12:43 +0300 | [diff] [blame] | 76 | assertEquals(thread, Thread.currentThread()) |
Dmitry Khalanskiy | 387d1dd | 2020-02-10 18:42:28 +0300 | [diff] [blame] | 77 | assertNull(result) |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 78 | expect(5) |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 79 | } |
Roman Elizarov | ca9d5be | 2017-04-20 19:23:18 +0300 | [diff] [blame] | 80 | finish(6) |
Roman Elizarov | 507f5d4 | 2017-04-19 19:15:34 +0300 | [diff] [blame] | 81 | } |
| 82 | } |