From f0bcccfe317d714866153777852126980f1d12dd Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Mon, 21 Mar 2016 11:14:30 +0100 Subject: [PATCH 01/18] Updated RxJava to version 1.1.2 --- projects/rx/rx.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/rx/rx.gradle b/projects/rx/rx.gradle index a1d8fdc..09cdc54 100644 --- a/projects/rx/rx.gradle +++ b/projects/rx/rx.gradle @@ -24,5 +24,5 @@ project.description = "Add Promise support to Rx" dependencies { compile project(':kovenant-core') - compile 'io.reactivex:rxjava:1.1.1' + compile 'io.reactivex:rxjava:1.1.2' } \ No newline at end of file From c4ee7f81721d60b9b444b794f10877c674bd0b74 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Mon, 28 Mar 2016 12:00:45 +0200 Subject: [PATCH 02/18] KOV-78 support sun.misc.Unsafe on older Dalvik systems (Android) --- projects/core/src/main/kotlin/cas-jvm.kt | 75 ++++++++++++++++++++---- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/projects/core/src/main/kotlin/cas-jvm.kt b/projects/core/src/main/kotlin/cas-jvm.kt index ebcf3c2..99f7d1c 100644 --- a/projects/core/src/main/kotlin/cas-jvm.kt +++ b/projects/core/src/main/kotlin/cas-jvm.kt @@ -1,6 +1,26 @@ +/* + * Copyright (c) 2016 Mark Platvoet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * THE SOFTWARE. + */ + package nl.komponents.kovenant.unsafe -import sun.misc.Unsafe import java.util.concurrent.atomic.AtomicReferenceFieldUpdater import kotlin.reflect.KClass @@ -29,23 +49,56 @@ class UnsafeAtomicReferenceFieldUpdater(targetClass: KClass override fun weakCompareAndSet(target: C, expected: V?, update: V?): Boolean = compareAndSet(target, expected, update) } +private val noUnsafeMarker = Any() +private @Volatile var unsafeInstance: Any? = null fun hasUnsafe(): Boolean { - try { - Class.forName("sun.misc.Unsafe") - return true - } catch(e: ClassNotFoundException) { - return false + if (unsafeInstance == null) { + loadUnsafe() } + println("has Unsafe: ${unsafeInstance != noUnsafeMarker}") + return unsafeInstance != noUnsafeMarker } -private fun getUnsafe(): Unsafe { +private fun loadUnsafe() { + println("loading unsafe") try { - val field = Unsafe::class.java.getDeclaredField("theUnsafe"); - field.isAccessible = true; - return field.get(null) as Unsafe; + val clazz = Class.forName("sun.misc.Unsafe") + + clazz.tryGetStaticField("theUnsafe") { + unsafeInstance = it ?: noUnsafeMarker + return + } + + //KOV-78: Name on old dalvik implementations + clazz.tryGetStaticField("THE_ONE") { + unsafeInstance = it ?: noUnsafeMarker + return + } + } catch(e: Exception) { + //ignore + } + unsafeInstance = noUnsafeMarker +} +private inline fun Class<*>.tryGetStaticField(name: String, onFound: (Any?) -> Unit) { + try { + val field = getDeclaredField(name) + field.isAccessible = true + val fieldValue = field.get(null) + onFound(fieldValue) } catch (e: Exception) { - throw RuntimeException("unsafe doesn't exist or is not accessible") + //ignore + } +} + +private fun getUnsafe(): sun.misc.Unsafe { + if (unsafeInstance == null) { + loadUnsafe() + } + if (unsafeInstance != noUnsafeMarker) { + return unsafeInstance as sun.misc.Unsafe } + + throw RuntimeException("unsafe doesn't exist or is not accessible") } \ No newline at end of file From 76ed7a9ed62b843c14fd08cebd8620475028b9f0 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Mon, 28 Mar 2016 13:38:11 +0200 Subject: [PATCH 03/18] KOV-78 support sun.misc.Unsafe on older Dalvik systems (Android), removed printlns --- projects/core/src/main/kotlin/cas-jvm.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/projects/core/src/main/kotlin/cas-jvm.kt b/projects/core/src/main/kotlin/cas-jvm.kt index 99f7d1c..f835306 100644 --- a/projects/core/src/main/kotlin/cas-jvm.kt +++ b/projects/core/src/main/kotlin/cas-jvm.kt @@ -56,12 +56,10 @@ fun hasUnsafe(): Boolean { if (unsafeInstance == null) { loadUnsafe() } - println("has Unsafe: ${unsafeInstance != noUnsafeMarker}") return unsafeInstance != noUnsafeMarker } private fun loadUnsafe() { - println("loading unsafe") try { val clazz = Class.forName("sun.misc.Unsafe") From 87f463942d1ea51de779d07762ef7b200c6d4fda Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Tue, 29 Mar 2016 08:36:06 +0200 Subject: [PATCH 04/18] Updated kotlin to version 1.0.1-1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f8f0a03..f752e7a 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ */ buildscript { - ext.kotlinVersion = '1.0.1' + ext.kotlinVersion = '1.0.1-1' ext.extraConfVersion = '2.2.+' repositories { From e17a2afebfc8a5d1dae989974528118c8697bae6 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Sat, 9 Apr 2016 20:41:30 +0200 Subject: [PATCH 05/18] KOV-80 --- docs/docs/android/config.md | 36 ++++++++++++++++++++--------------- docs/docs/android/features.md | 2 ++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/docs/docs/android/config.md b/docs/docs/android/config.md index c9a96b4..90728e9 100644 --- a/docs/docs/android/config.md +++ b/docs/docs/android/config.md @@ -10,34 +10,40 @@ a constant creation and destruction of threads. That's not good for responsivene So we need to keep our threads alive. That also implies we need to tell when to shut them down again. Kovenant introduces two convenience functions, `startKovenant()` and `stopKovenant()`, to keep threads alive and shut them -down at the proper time again. +down at the proper time again. This only needs to be done once per application. -So it all comes down to this: +So the recommended way to setup (and shutdown) Kovenant is by providing your own [`Application`](http://developer.android.com/reference/android/app/Application.html) implementation: ```kt -public class MainActivity : ... { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(...) - +class MyApplication : Application() { + override fun onCreate() { + super.onCreate() // Configure Kovenant with standard dispatchers + // suitable for an Android environment. startKovenant() - } - ... - - override fun onDestroy() { - // Dispose of the Kovenant thread pools - // for quicker shutdown you could use - // force=true, which ignores all current + override fun onTerminate() { + super.onTerminate() + // Dispose of the Kovenant thread pools. + // For quicker shutdown you could use + // `force=true`, which ignores all current // scheduled tasks stopKovenant() - super.onDestroy() } } ``` +Don't forget to properly setup your application in your `AndroidManifest.xml`: + +```xml + + + + +``` + Note that `stopKovenant(force: Boolean = false)` also has a `force` parameter that defaults to `false`. So by default all the queues are depleted. If you don't want this you can always use `force = true` to immediately shutdown the Dispatchers. `stopKovenant` never blocks though. diff --git a/docs/docs/android/features.md b/docs/docs/android/features.md index 276823c..fc2ba50 100644 --- a/docs/docs/android/features.md +++ b/docs/docs/android/features.md @@ -2,6 +2,8 @@ part of [`kovenant-android`](../index.md#artifacts) --- +> Please consult the [configuration section](config.md) for properly setting up Kovenant for Android. + While Kovenant is perfectly usable on Android as-is, there are a couple things that are specific to the platform. One is that Android applications can only interact with the interface through the main thread. By default Kovenant operates on its own maintained pool of threads and thus can't update the UI. From 44980a0df19522e8b763e8185d8b38f61b6f035c Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Wed, 13 Apr 2016 22:18:48 +0200 Subject: [PATCH 06/18] RXJava 1.1.3 --- projects/core/src/test/kotlin/examples/bla.kt | 19 +++++++++++++++++++ projects/rx/rx.gradle | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 projects/core/src/test/kotlin/examples/bla.kt diff --git a/projects/core/src/test/kotlin/examples/bla.kt b/projects/core/src/test/kotlin/examples/bla.kt new file mode 100644 index 0000000..5533c6d --- /dev/null +++ b/projects/core/src/test/kotlin/examples/bla.kt @@ -0,0 +1,19 @@ +package examples + +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.task +import nl.komponents.kovenant.then +import nl.komponents.kovenant.unwrap + +fun main(args: Array) { + val l = task { + Promise.of(1) + }.unwrap().then { + Promise.of(it + 1) + }.unwrap() + + l success { + println(it) + } +} + diff --git a/projects/rx/rx.gradle b/projects/rx/rx.gradle index 09cdc54..2ef7569 100644 --- a/projects/rx/rx.gradle +++ b/projects/rx/rx.gradle @@ -24,5 +24,5 @@ project.description = "Add Promise support to Rx" dependencies { compile project(':kovenant-core') - compile 'io.reactivex:rxjava:1.1.2' + compile 'io.reactivex:rxjava:1.1.3' } \ No newline at end of file From 928b3d8b68c769e57ffc32cec4c3c71e41700acb Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Fri, 13 May 2016 23:04:26 +0200 Subject: [PATCH 07/18] Version update to 1.0.2 --- README.md | 2 +- build.gradle | 2 +- docs/docs/index.md | 2 +- projects/core/src/main/kotlin/dispatcher-jvm.kt | 1 - projects/rx/rx.gradle | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 06b5254..d99b318 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ task { "world" } and task { "Hello" } success { Please refer to the [Kovenant](http://kovenant.komponents.nl) site for API usage and more. ## Getting started -Build against Kotlin: `1.0.1-2`. +Build against Kotlin: `1.0.2`. Source and target compatibility is `1.6` ###Gradle diff --git a/build.gradle b/build.gradle index 00efb24..42a73cd 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ */ buildscript { - ext.kotlinVersion = '1.0.1-2' + ext.kotlinVersion = '1.0.2' ext.extraConfVersion = '2.2.+' repositories { diff --git a/docs/docs/index.md b/docs/docs/index.md index 2c4ddd0..eaa21bb 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -25,7 +25,7 @@ Developed with the following [goals](misc/goals.md) in mind. * **Dependency free**: when not counting kotlin std ## Getting started -Build against Kotlin: `1.0.1-2`. +Build against Kotlin: `1.0.2`. Source and target compatibility is `1.6` ###Gradle diff --git a/projects/core/src/main/kotlin/dispatcher-jvm.kt b/projects/core/src/main/kotlin/dispatcher-jvm.kt index 513a097..479b76c 100644 --- a/projects/core/src/main/kotlin/dispatcher-jvm.kt +++ b/projects/core/src/main/kotlin/dispatcher-jvm.kt @@ -219,7 +219,6 @@ private class NonBlockingDispatcher(val name: String, val function = workQueue.poll() ?: return remains remains.add(function) } while (true) - throw IllegalStateException("unreachable") } override fun tryCancel(task: () -> Unit): Boolean { diff --git a/projects/rx/rx.gradle b/projects/rx/rx.gradle index 2ef7569..6aa380d 100644 --- a/projects/rx/rx.gradle +++ b/projects/rx/rx.gradle @@ -24,5 +24,5 @@ project.description = "Add Promise support to Rx" dependencies { compile project(':kovenant-core') - compile 'io.reactivex:rxjava:1.1.3' + compile 'io.reactivex:rxjava:1.1.5' } \ No newline at end of file From 0707fff60caa9904dae3d7bcbbb0c15ba59d403f Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 9 Jun 2016 21:22:51 +0200 Subject: [PATCH 08/18] =?UTF-8?q?numberOfTasks=20has=20been=20renamed=20to?= =?UTF-8?q?=20concurrentTasks,=20reported=20by=20Elviss=20Ku=C5=A1tans?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/api/core_config.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/api/core_config.md b/docs/docs/api/core_config.md index b715a8b..643a013 100644 --- a/docs/docs/api/core_config.md +++ b/docs/docs/api/core_config.md @@ -67,7 +67,7 @@ Let me state upfront that this method is *not threadsafe*. ```kt buildDispatcher { name = "Dispatcher Name" - numberOfThreads = 1 + concurrentTasks = 1 exceptionHandler = ...// (Exception) -> Unit errorHandler = ... // (Throwable) -> Unit pollStrategy { ... } @@ -76,8 +76,8 @@ buildDispatcher { **name** Sets the name of this `Dispatcher`. Is also used as thread names appended by a number -**numberOfThreads** -The maximum number of threads this `Dispatcher` concurrently keeps alive. Note that the actual +**concurrentTasks** +The maximum number of tasks this `Dispatcher` concurrently keeps alive. Note that the actual number of threads can be lower and depends on how much work is offered. Also, during the lifetime of the `Dispatcher` the number ov threads instantiated can be far greater because threads can also be destroyed. @@ -104,7 +104,7 @@ can be chained and are executed in order of configuration. ```kt val dispatcher = buildDispatcher { name = "Bob the builder" - numberOfThreads = 1 + concurrentTasks = 1 pollStrategy { yielding(numberOfPolls = 1000) From d7a242b27eb1f28799c08a9a3bdf08e8ec8f11bd Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Mon, 13 Jun 2016 20:51:36 +0200 Subject: [PATCH 09/18] Cleanup --- projects/core/src/test/kotlin/examples/bla.kt | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 projects/core/src/test/kotlin/examples/bla.kt diff --git a/projects/core/src/test/kotlin/examples/bla.kt b/projects/core/src/test/kotlin/examples/bla.kt deleted file mode 100644 index 5533c6d..0000000 --- a/projects/core/src/test/kotlin/examples/bla.kt +++ /dev/null @@ -1,19 +0,0 @@ -package examples - -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.task -import nl.komponents.kovenant.then -import nl.komponents.kovenant.unwrap - -fun main(args: Array) { - val l = task { - Promise.of(1) - }.unwrap().then { - Promise.of(it + 1) - }.unwrap() - - l success { - println(it) - } -} - From 0a68fee66af7d8b08bc77d3107befa20b0d4c985 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Fri, 17 Jun 2016 21:52:17 +0200 Subject: [PATCH 10/18] KOV-87 Cancelable deferred --- projects/core/src/main/kotlin/context-api.kt | 9 +- projects/core/src/main/kotlin/context-jvm.kt | 2 + projects/core/src/main/kotlin/promises-api.kt | 11 +++ projects/core/src/main/kotlin/promises-jvm.kt | 97 +++++++++++++++++++ .../kotlin/examples/cancelableDeferred.kt | 37 +++++++ 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 projects/core/src/test/kotlin/examples/cancelableDeferred.kt diff --git a/projects/core/src/main/kotlin/context-api.kt b/projects/core/src/main/kotlin/context-api.kt index 155892f..cc7c949 100644 --- a/projects/core/src/main/kotlin/context-api.kt +++ b/projects/core/src/main/kotlin/context-api.kt @@ -39,10 +39,14 @@ object Kovenant { fun deferred(context: Context = Kovenant.context): Deferred = concrete.deferred(context) + fun deferred(context: Context = Kovenant.context, onCancelled: (E) -> Unit): Deferred = concrete.deferred(context, onCancelled) + fun stop(force: Boolean = false, timeOutMs: Long = 0, block: Boolean = true): List<() -> Unit> { return context.stop(force, timeOutMs, block) } + fun cancel(promise: Promise<*, E>, error: E): Boolean = promise is CancelablePromise && promise.cancel(error) + } interface Context { @@ -82,7 +86,7 @@ interface ReconfigurableContext : MutableContext { interface DispatcherContext { companion object { fun create(dispatcher: Dispatcher, - errorHandler: (Exception) -> Unit): DispatcherContext + errorHandler: (Exception) -> Unit): DispatcherContext = StaticDispatcherContext(dispatcher, errorHandler) } @@ -137,7 +141,8 @@ fun Kovenant.testMode(failures: (Throwable) -> Unit = { throw it }) { workerContext.errorHandler = failures multipleCompletion = { - first, second -> failures(KovenantException("multiple completion: first = $first, second = $second")) + first, second -> + failures(KovenantException("multiple completion: first = $first, second = $second")) } } } diff --git a/projects/core/src/main/kotlin/context-jvm.kt b/projects/core/src/main/kotlin/context-jvm.kt index 4af1eab..0ca29ea 100644 --- a/projects/core/src/main/kotlin/context-jvm.kt +++ b/projects/core/src/main/kotlin/context-jvm.kt @@ -67,6 +67,8 @@ class ConcreteKovenant { fun deferred(context: Context = Kovenant.context): Deferred = concreteDeferred(context) + fun deferred(context: Context = Kovenant.context, onCancelled: (E) -> Unit): Deferred = concreteDeferred(context, onCancelled) + private class ThreadSafeContext() : ReconfigurableContext { private val multipleCompletionDelegate = ThreadSafeLazyVar<(Any?, Any?) -> Unit> { diff --git a/projects/core/src/main/kotlin/promises-api.kt b/projects/core/src/main/kotlin/promises-api.kt index 286666b..e2dc4e2 100644 --- a/projects/core/src/main/kotlin/promises-api.kt +++ b/projects/core/src/main/kotlin/promises-api.kt @@ -19,6 +19,7 @@ * THE SOFTWARE. */ @file:JvmName("KovenantApi") + package nl.komponents.kovenant @@ -269,6 +270,16 @@ interface Promise { fun deferred(context: Context = Kovenant.context): Deferred = Kovenant.deferred(context) +/** + * Creates a new [Deferred] instance with a promise that is a [CancelablePromise] + * + * @param context the context on which the associated [Promise] operates on + * @param onCancelled called when the [Promise] has been cancelled + * @return newly created [Deferred] + */ +fun deferred(context: Context = Kovenant.context, onCancelled: (E) -> Unit): Deferred = Kovenant.deferred(context, onCancelled) + + /** * Executes the given task on the work [DispatcherContext] of provided [Context] and returns a [Promise]. * Any Ecxeption is considered a failure. diff --git a/projects/core/src/main/kotlin/promises-jvm.kt b/projects/core/src/main/kotlin/promises-jvm.kt index e81ea68..7b04924 100644 --- a/projects/core/src/main/kotlin/promises-jvm.kt +++ b/projects/core/src/main/kotlin/promises-jvm.kt @@ -38,6 +38,8 @@ internal fun concreteFailedPromise(context: Context, value: E): Promise concreteDeferred(context: Context): Deferred = DeferredPromise(context) +internal fun concreteDeferred(context: Context, onCancelled: (E) -> Unit): Deferred = CancelableDeferredPromise(context, onCancelled) + private class SuccessfulPromise(context: Context, value: V) : AbstractPromise(context) { init { trySetSuccessResult(value) @@ -209,6 +211,101 @@ private class DeferredPromise(context: Context) : AbstractPromise(co override val promise: Promise = object : Promise by this {} } +private class CancelableDeferredPromise(context: Context, onCancelled: (E) -> Unit) : + AbstractPromise(context), Deferred, + CancelablePromise { + + companion object { + //prefer ints over enums for class count + //matters on Android + private val unresolved = 0 + private val cancelled = 1 + private val failed = 2 + private val success = 3 + } + + //allow callback to be cleaned. Promise can live a long time so avoid + //callback holding to resources not important anymore + private @Volatile var cb: ((E) -> Unit)? = onCancelled + + private @Volatile var state = unresolved + + override fun resolve(value: V) { + while (state == unresolved) { + if (trySetSuccessResult(value)) { + state = success + fireSuccess(value) + cb = null + return + } + } + + //Only report multiple completion if this has not been cancelled + //since in race situations this can simply happen. + if (state != cancelled) { + multipleCompletion(value) + } + } + + override fun reject(error: E) { + while (state == unresolved) { + if (trySetFailResult(error)) { + state = failed + fireFail(error) + cb = null + return + } + } + + //Only report multiple completion if this has not been cancelled + //since in race situations this can simply happen. + if (state != cancelled) { + multipleCompletion(error) + } + } + + override fun cancel(error: E): Boolean { + while (state == unresolved) { + if (trySetFailResult(error)) { + state = cancelled + + //Try to actually cancel before firing the callbacks. + //this to avoid situations where someone expects an happens-before + + val exc = tryCancelCb(error) + + fireFail(error) + cb = null + + return when (exc) { + null -> true + else -> throw KovenantException("Promise cancelled but cancel might not have succeeded due to exception in callback", exc) + } + } + } + return false + } + + private fun tryCancelCb(error: E): Exception? { + try { + cb?.invoke(error) + } catch (e: Exception) { + return e + } + return null + } + + //Only call this method if we know resolving is eminent. + private fun multipleCompletion(newValue: Any?) { + while (!isDoneInternal()) { + Thread.`yield`() + } + context.multipleCompletion(rawValue(), newValue) + } + + override val promise: Promise = object : CancelablePromise by this {} +} + private abstract class AbstractPromise(override val context: Context) : Promise { companion object { diff --git a/projects/core/src/test/kotlin/examples/cancelableDeferred.kt b/projects/core/src/test/kotlin/examples/cancelableDeferred.kt new file mode 100644 index 0000000..4121daf --- /dev/null +++ b/projects/core/src/test/kotlin/examples/cancelableDeferred.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Mark Platvoet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * THE SOFTWARE. + */ + +package examples.cancelableDeferred + +import nl.komponents.kovenant.Kovenant +import nl.komponents.kovenant.deferred + + +fun main(args: Array) { + val deferred = deferred { + //callback method to receive notification + //when cancel is requested + println("I'm cancelled by $it") + } + + Kovenant.cancel(deferred.promise, "test method") +} + From a0d1092893a8d80ef3e61ab98fa867ef2d5fbb19 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Sun, 19 Jun 2016 09:34:04 +0200 Subject: [PATCH 11/18] Updated Kotlin to 1.0.2-1 and rxjava to 1.1.6 --- build.gradle | 2 +- projects/rx/rx.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 42a73cd..4fe8426 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ */ buildscript { - ext.kotlinVersion = '1.0.2' + ext.kotlinVersion = '1.0.2-1' ext.extraConfVersion = '2.2.+' repositories { diff --git a/projects/rx/rx.gradle b/projects/rx/rx.gradle index 6aa380d..876613b 100644 --- a/projects/rx/rx.gradle +++ b/projects/rx/rx.gradle @@ -24,5 +24,5 @@ project.description = "Add Promise support to Rx" dependencies { compile project(':kovenant-core') - compile 'io.reactivex:rxjava:1.1.5' + compile 'io.reactivex:rxjava:1.1.6' } \ No newline at end of file From 8cd26b2c8aec26b6b306718f4a22d2f7f0f576de Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 20:17:02 +0200 Subject: [PATCH 12/18] Updated Kotlin to 1.0.3 --- README.md | 2 +- build.gradle | 2 +- docs/docs/index.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d99b318..5f6e9ba 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ task { "world" } and task { "Hello" } success { Please refer to the [Kovenant](http://kovenant.komponents.nl) site for API usage and more. ## Getting started -Build against Kotlin: `1.0.2`. +Build against Kotlin: `1.0.3`. Source and target compatibility is `1.6` ###Gradle diff --git a/build.gradle b/build.gradle index 4fe8426..58363dd 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ */ buildscript { - ext.kotlinVersion = '1.0.2-1' + ext.kotlinVersion = '1.0.3' ext.extraConfVersion = '2.2.+' repositories { diff --git a/docs/docs/index.md b/docs/docs/index.md index eaa21bb..575d2de 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -25,7 +25,7 @@ Developed with the following [goals](misc/goals.md) in mind. * **Dependency free**: when not counting kotlin std ## Getting started -Build against Kotlin: `1.0.2`. +Build against Kotlin: `1.0.3`. Source and target compatibility is `1.6` ###Gradle From 01c3aa8f10a12646059854b317784fff00806b0c Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 20:35:25 +0200 Subject: [PATCH 13/18] Progress updated to 1.0.3 --- projects/progress/progress.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/progress/progress.gradle b/projects/progress/progress.gradle index 73d7fed..a46b663 100644 --- a/projects/progress/progress.gradle +++ b/projects/progress/progress.gradle @@ -24,5 +24,5 @@ project.description = "Progress tracking Kovenant extensions" dependencies { compile project(':kovenant-core') - compile 'nl.komponents.progress:progress-core:1.0.2' + compile 'nl.komponents.progress:progress-core:1.0.3' } \ No newline at end of file From f96a0224e4e39dc3aec7d9e9e668225f6b5582e1 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 20:35:54 +0200 Subject: [PATCH 14/18] Progress updated to 1.0.3 --- projects/progress/progress.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/progress/progress.gradle b/projects/progress/progress.gradle index 73d7fed..a46b663 100644 --- a/projects/progress/progress.gradle +++ b/projects/progress/progress.gradle @@ -24,5 +24,5 @@ project.description = "Progress tracking Kovenant extensions" dependencies { compile project(':kovenant-core') - compile 'nl.komponents.progress:progress-core:1.0.2' + compile 'nl.komponents.progress:progress-core:1.0.3' } \ No newline at end of file From 49161896986427572316e47e6ecd563feae9a9b2 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 20:48:03 +0200 Subject: [PATCH 15/18] Added documentation --- docs/docs/api/core_usage.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/docs/api/core_usage.md b/docs/docs/api/core_usage.md index a1fc7a3..ce5afd0 100644 --- a/docs/docs/api/core_usage.md +++ b/docs/docs/api/core_usage.md @@ -45,6 +45,26 @@ fun handlePromise(promise: Promise) { } ``` +--- + +##Cancelable Deferred +By default the promise returned by a `Deferred` is _not_ a [`CancelablePromise`](#cancel). Simply because there is no way +to tell the owner of the `Promise` that a cancel has been requested. If we however provide a callback to the `deferred` function +we are able to cancel. What cancelling means is of course up to the one owning the `Deferred`. + + +```kt +val deferred = deferred { + //callback method to receive notification + //when cancel is requested + println("I'm cancelled by $it") +} + +//Convenience method for trying to cancel promises +Kovenant.cancel(deferred.promise, "test method") +``` + + --- ##Callbacks From 1b4a4e256ec6d624946af7f4357a17ce98445d49 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 20:58:29 +0200 Subject: [PATCH 16/18] Version bump to 3.3.0, changelog updated --- README.md | 4 ++-- docs/docs/changelog.md | 12 ++++++++++++ docs/docs/index.md | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f6e9ba..7e70a28 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Source and target compatibility is `1.6` ###Gradle ```groovy dependencies { - compile 'nl.komponents.kovenant:kovenant:3.2.2' + compile 'nl.komponents.kovenant:kovenant:3.3.0' } ``` @@ -32,7 +32,7 @@ dependencies { nl.komponents.kovenant kovenant - 3.2.2 + 3.3.0 ``` diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index f1d8dfb..c3c0970 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -3,6 +3,18 @@ Changelog of Kovenant. Complying to [Semantic Versioning](http://semver.org). Please refer to [roadmap](roadmap.md) for upcoming releases. +##v3.3.0 + +**general** + +* Update to Kotlin 1.0.3 +* [KOV-80](http://issues.komponents.nl/youtrack/issue/KOV-80) enhance/correct the documentation + +**core** + +* [KOV-87](http://issues.komponents.nl/youtrack/issue/KOV-87) Cancelable deferred + + ##v3.2.2 **general** diff --git a/docs/docs/index.md b/docs/docs/index.md index 575d2de..7b52cfa 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -31,7 +31,7 @@ Source and target compatibility is `1.6` ###Gradle ```groovy dependencies { - compile 'nl.komponents.kovenant:kovenant:3.2.2' + compile 'nl.komponents.kovenant:kovenant:3.3.0' } ``` @@ -40,7 +40,7 @@ dependencies { nl.komponents.kovenant kovenant - 3.2.2 + 3.3.0 ``` From 4ed4763ba209ccef01ece624f828e4f12efae552 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 20:58:59 +0200 Subject: [PATCH 17/18] Version bump to 3.3.0, changelog updated, updated roadmap --- docs/docs/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/roadmap.md b/docs/docs/roadmap.md index 9bc6450..604eafe 100644 --- a/docs/docs/roadmap.md +++ b/docs/docs/roadmap.md @@ -5,7 +5,7 @@ Input is always welcome. --- -##v3.3.0 +##v3.4.0 **expected** From 16af75df545d5848317a6b3f5f9a5f375d23da55 Mon Sep 17 00:00:00 2001 From: mplatvoet Date: Thu, 30 Jun 2016 21:06:00 +0200 Subject: [PATCH 18/18] Version bump to 3.3.0, changelog updated, updated roadmap --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 58363dd..179bdaa 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ buildscript { allprojects { ext { - appVersion = '3.2.2' + appVersion = '3.3.0' appGroup = 'nl.komponents.kovenant'