Concurrent API update. (#1949)

This commit is contained in:
Nikolay Igotti
2018-08-29 10:20:07 +03:00
committed by GitHub
parent 63cf6777b1
commit f47fe79ef8
42 changed files with 587 additions and 417 deletions
@@ -14,7 +14,7 @@ import kotlin.test.*
@Test
fun testRandomWorkers() {
val seed = getTimeMillis()
val workers = Array(5, { _ -> startWorker()})
val workers = Array(5, { _ -> Worker.start() })
val attempts = 3
val results = Array(attempts, { ArrayList<Int>() } )
@@ -22,7 +22,8 @@ fun testRandomWorkers() {
Random.seed = seed
// Produce a list of random numbers in each worker
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { workerIndex }) { input ->
workers[workerIndex].execute(TransferMode.SAFE, { workerIndex }) {
input ->
Array(10, { Random.nextInt() }).toList()
}
})
@@ -30,11 +31,11 @@ fun testRandomWorkers() {
val futureSet = futures.toSet()
for (i in 0 until futureSet.size) {
val ready = futureSet.waitForMultipleFutures(10000)
ready.forEach { results[attempt].addAll(it.result()) }
ready.forEach { results[attempt].addAll(it.result) }
}
}
workers.forEach {
it.requestTermination().consume { _ -> }
it.requestTermination().result
}
}
+27 -27
View File
@@ -12,54 +12,54 @@ import kotlin.native.concurrent.*
fun test1(workers: Array<Worker>) {
val atomic = AtomicInt(15)
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { atomic }) {
input -> input.increment()
workers[workerIndex].execute(TransferMode.SAFE, { atomic }) {
input -> input.addAndGet(1)
}
})
futures.forEach {
it.result()
it.result
}
println(atomic.get())
println(atomic.value)
}
fun test2(workers: Array<Worker>) {
val atomic = AtomicInt(1)
val counter = AtomicInt(0)
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { Triple(atomic, workerIndex, counter) }) {
workers[workerIndex].execute(TransferMode.SAFE, { Triple(atomic, workerIndex, counter) }) {
(place, index, result) ->
// Here we simulate mutex using [place] location to store tag of the current worker.
// When it is negative - worker executes exclusively.
val tag = index + 1
while (place.compareAndSwap(tag, -tag) != tag) {}
val ok1 = result.increment() == index + 1
val ok1 = result.addAndGet(1) == index + 1
// Now, let the next worker run.
val ok2 = place.compareAndSwap(-tag, tag + 1) == -tag
ok1 && ok2
}
})
futures.forEach {
assertEquals(it.result(), true)
assertEquals(it.result, true)
}
println(counter.get())
println(counter.value)
}
data class Data(val value: Int)
fun test3(workers: Array<Worker>) {
val common = AtomicReference<Data>()
val common = AtomicReference<Data?>(null)
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { Pair(common, workerIndex) }) {
workers[workerIndex].execute(TransferMode.SAFE, { Pair(common, workerIndex) }) {
(place, index) ->
val mine = Data(index).freeze()
// Try to publish our own data, until successful, in a tight loop.
while (place.compareAndSwap(null, mine) != null) {}
while (!place.compareAndSet(null, mine)) {}
}
})
val seen = mutableSetOf<Data>()
for (i in 0 until workers.size) {
do {
val current = common.get()
val current = common.value
if (current != null && !seen.contains(current)) {
seen += current
// Let others publish.
@@ -69,7 +69,7 @@ fun test3(workers: Array<Worker>) {
} while (true)
}
futures.forEach {
it.result()
it.result
}
assertEquals(seen.size, workers.size)
}
@@ -79,33 +79,33 @@ fun test4() {
AtomicReference(Data(1))
}
assertFailsWith<InvalidMutabilityException> {
AtomicReference<Data>().compareAndSwap(null, Data(2))
AtomicReference<Data?>(null).compareAndSwap(null, Data(2))
}
}
fun test5() {
assertFailsWith<InvalidMutabilityException> {
AtomicReference<Data>().set(Data(2))
AtomicReference<Data?>(null).value = Data(2)
}
val ref = AtomicReference<Data>()
val ref = AtomicReference<Data?>(null)
val value = Data(3).freeze()
assertEquals(null, ref.get())
ref.set(value)
assertEquals(3, ref.get()!!.value)
assertEquals(null, ref.value)
ref.value = value
assertEquals(3, ref.value!!.value)
}
fun test6() {
val int = AtomicInt()
int.set(239)
assertEquals(239, int.get())
val long = AtomicLong()
long.set(239L)
assertEquals(239L, long.get())
val int = AtomicInt(0)
int.value = 239
assertEquals(239, int.value)
val long = AtomicLong(0)
long.value = 239L
assertEquals(239L, long.value)
}
@Test fun runTest() {
val COUNT = 20
val workers = Array(COUNT, { _ -> startWorker()})
val workers = Array(COUNT, { _ -> Worker.start()})
test1(workers)
test2(workers)
@@ -115,7 +115,7 @@ fun test6() {
test6()
workers.forEach {
it.requestTermination().consume { _ -> }
it.requestTermination().result
}
println("OK")
}
@@ -17,8 +17,8 @@ data class Foo(val kind: A)
// Enums are shared between threads so identity should be kept.
@Test
fun runTest() {
val result = startWorker().schedule(TransferMode.CHECKED, { Foo(A.B) }, { input ->
val result = Worker.start().execute(TransferMode.SAFE, { Foo(A.B) }, { input ->
input.kind == A.B
}).result()
}).result
println(result)
}
@@ -14,12 +14,12 @@ data class SharedDataMember(val double: Double)
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
@Test fun runTest() {
val worker = startWorker()
val worker = Worker.start()
// Create immutable shared data.
val immutable = SharedData("Hello", 10, SharedDataMember(0.1)).freeze()
println("frozen bit is ${immutable.isFrozen}")
val future = worker.schedule(TransferMode.CHECKED, { immutable } ) {
val future = worker.execute(TransferMode.SAFE, { immutable } ) {
input ->
println("Worker: $input")
input
@@ -27,6 +27,6 @@ data class SharedData(val string: String, val int: Int, val member: SharedDataMe
future.consume {
result -> println("Main: $result")
}
worker.requestTermination().result()
worker.requestTermination().result
println("OK")
}
+10 -10
View File
@@ -59,32 +59,32 @@ data class Data(var int: Int)
assertFailsWith<InvalidMutabilityException> { a8[1] = 2.0 }
// Ensure that String and integral boxes are frozen by default, by passing local to the worker.
val worker = startWorker()
val worker = Worker.start()
var data: Any = "Hello" + " " + "world"
assert(data.isFrozen)
worker.schedule(TransferMode.CHECKED, { data } ) {
worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker 1: $input")
}.result()
}.result
data = 42
assert(data.isFrozen)
worker.schedule(TransferMode.CHECKED, { data } ) {
worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker2: $input")
}.result()
}.result
data = 239.0
assert(data.isFrozen)
worker.schedule(TransferMode.CHECKED, { data } ) {
worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker3: $input")
}.result()
}.result
data = 'a'
assert(data.isFrozen)
worker.schedule(TransferMode.CHECKED, { data } ) {
worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker4: $input")
}.result()
}.result
worker.requestTermination().result()
worker.requestTermination().result
println("OK")
}
@@ -36,9 +36,9 @@ fun testSingleData(workers: Array<Worker>) {
val set = mutableSetOf<Any?>()
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { "" }) { _ -> Immutable2.y }
workers[workerIndex].execute(TransferMode.SAFE, { "" }) { _ -> Immutable2.y }
})
futures.forEach { set += it.result() }
futures.forEach { set += it.result }
}
assertEquals(set.size, 1)
assertEquals(set.single(), Immutable2.y)
@@ -48,9 +48,9 @@ fun testFrozenLazy(workers: Array<Worker>) {
val set = mutableSetOf<Int>()
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { "" }) { _ -> Immutable3.x }
workers[workerIndex].execute(TransferMode.SAFE, { "" }) { _ -> Immutable3.x }
})
futures.forEach { set += it.result() }
futures.forEach { set += it.result }
}
assertEquals(1, set.size)
assertEquals(Immutable3.x, set.single())
@@ -77,7 +77,7 @@ fun testLiquidLazy() {
assertEquals(42, Immutable.x)
val COUNT = 5
val workers = Array(COUNT, { _ -> startWorker()})
val workers = Array(COUNT, { _ -> Worker.start()})
testSingleData(workers)
testFrozenLazy(workers)
testLiquidLazy()
@@ -10,13 +10,13 @@ import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = startWorker()
val future = worker.schedule(TransferMode.CHECKED, { "Input" }) {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { "Input" }) {
input -> input + " processed"
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -11,11 +11,11 @@ import kotlin.native.concurrent.*
@Test fun runTest() {
val COUNT = 5
val workers = Array(COUNT, { _ -> startWorker()})
val workers = Array(COUNT, { _ -> Worker.start()})
for (attempt in 1 .. 3) {
val futures = Array(workers.size,
{ i -> workers[i].schedule(TransferMode.CHECKED, { "$attempt: Input $i" })
{ i -> workers[i].execute(TransferMode.SAFE, { "$attempt: Input $i" })
{ input -> input + " processed" }
})
futures.forEachIndexed { index, future ->
@@ -29,7 +29,7 @@ import kotlin.native.concurrent.*
}
}
workers.forEach {
it.requestTermination().consume { _ -> }
it.requestTermination().result
}
println("OK")
}
@@ -14,10 +14,10 @@ data class WorkerResult(val intResult: Int, val stringResult: String)
@Test fun runTest() {
val COUNT = 5
val workers = Array(COUNT, { _ -> startWorker()})
val workers = Array(COUNT, { _ -> Worker.start()})
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex -> workers[workerIndex].schedule(TransferMode.CHECKED, {
val futures = Array(workers.size, { workerIndex -> workers[workerIndex].execute(TransferMode.SAFE, {
WorkerArgument(workerIndex, "attempt $attempt") }) { input ->
var sum = 0
for (i in 0..input.intParam * 1000) {
@@ -33,12 +33,13 @@ data class WorkerResult(val intResult: Int, val stringResult: String)
ready.forEach {
it.consume { result ->
if (result.stringResult != "attempt $attempt result") throw Error("Unexpected $result")
consumed++ }
consumed++
}
}
}
}
workers.forEach {
it.requestTermination().consume { _ -> }
it.requestTermination().result
}
println("OK")
}
@@ -18,19 +18,19 @@ data class WorkerResult(val intResult: Int, val stringResult: String)
}
fun main(args: Array<String>) {
val worker = startWorker()
val worker = Worker.start()
val dataParam = DataParam(17)
val future = try {
worker.schedule(TransferMode.CHECKED,
{ WorkerArgument(42, dataParam) },
{ input -> WorkerResult(input.intParam, input.dataParam.toString() + " result") }
)
worker.execute(TransferMode.SAFE,
{ WorkerArgument(42, dataParam) }) {
input -> WorkerResult(input.intParam, input.dataParam.toString() + " result")
}
} catch (e: IllegalStateException) {
null
}
if (future != null)
println("Fail 1")
if (dataParam.int != 17) println("Fail 2")
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -10,13 +10,13 @@ import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = startWorker()
val future = worker.schedule(TransferMode.CHECKED, { 41 }) {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { 41 }) {
input -> input + 1
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -10,13 +10,13 @@ import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = startWorker()
val future = worker.schedule(TransferMode.CHECKED, { "zzz" }) {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { "zzz" }) {
input -> input.length
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -10,13 +10,13 @@ import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = startWorker()
val future = worker.schedule(TransferMode.CHECKED, { 42 }) {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { 42 }) {
input -> input.toString()
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -10,13 +10,13 @@ import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = startWorker()
val future = worker.schedule(TransferMode.CHECKED, { "Input" }) {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { "Input" }) {
input -> println(input)
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -14,19 +14,19 @@ data class SharedDataMember(val double: Double)
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
@Test fun runTest() {
val worker = startWorker()
val worker = Worker.start()
// Here we do rather strange thing. To test object detach API we detach object graph,
// pass C pointer as a value to worker, where we manually reattached passed value.
val future = worker.schedule(TransferMode.CHECKED, {
detachObjectGraph { SharedData("Hello", 10, SharedDataMember(0.1)) }
} ) {
inputC ->
val input = attachObjectGraph<SharedData>(inputC)
// pass detached graph to a worker, where we manually reattached passed value.
val future = worker.execute(TransferMode.SAFE, {
DetachedObjectGraph { SharedData("Hello", 10, SharedDataMember(0.1)) }.asCPointer()
}) {
inputDetached ->
val input = DetachedObjectGraph<SharedData>(inputDetached).attach()
println(input)
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().consume { _ -> }
worker.requestTermination().result
println("OK")
}
@@ -11,14 +11,14 @@ import kotlin.native.concurrent.*
@Test fun runTest() {
withLock { println("zzz") }
val worker = startWorker()
val future = worker.schedule(TransferMode.CHECKED, {}) {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
withLock {
println("42")
}
}
future.result()
worker.requestTermination().result()
future.result
worker.requestTermination().result
println("OK")
}