Skip to content

Commit 328f537

Browse files
authored
enforce same thread on getState() (#34)
* same thread enforcement on store functions * update docs with Threading info
1 parent 98b20be commit 328f537

File tree

21 files changed

+967
-616
lines changed

21 files changed

+967
-616
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
## Unreleased
2+
3+
## [0.3.0] - 2019-12-16
4+
5+
### Added
6+
- thread enforcement
7+
18
## [0.2.9] - 2019-11-23
29

310
### Changed
411
- update Kotlin to 1.3.60
5-

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ kotlin {
4444
sourceSets {
4545
commonMain { // <--- name may vary on your project
4646
dependencies {
47-
implementation "org.reduxkotlin:redux-kotlin:0.2.9"
47+
implementation "org.reduxkotlin:redux-kotlin:0.3.0"
4848
}
4949
}
5050
}
5151
```
5252

5353
For JVM only:
5454
```
55-
implementation "org.reduxkotlin:redux-kotlin-jvm:0.2.9"
55+
implementation "org.reduxkotlin:redux-kotlin-jvm:0.3.0"
5656
```
5757

5858
Usage is very similar to JS Redux and those docs will be useful https://redux.js.org/. These docs are not an intro to Redux, and just documentation on Kotlin specific bits. For more info on Redux in general, check out https://redux.js.org/.

examples/counter/android/src/main/java/org/reduxkotlin/example/counter/MainActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.os.Handler
55
import androidx.appcompat.app.AppCompatActivity
66
import kotlinx.android.synthetic.main.activity_main.*
77
import org.reduxkotlin.StoreSubscription
8-
import org.reduxkotlin.combineReducers
98
import org.reduxkotlin.createStore
109
import org.reduxkotlin.examples.counter.Decrement
1110
import org.reduxkotlin.examples.counter.Increment

lib/build.gradle

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ apply plugin: 'kotlin-multiplatform'
77
archivesBaseName = 'redux-kotlin'
88

99
group 'org.reduxkotlin'
10-
version '0.2.9'
10+
version '0.3.0'
1111

1212
kotlin {
1313
jvm()
@@ -87,6 +87,24 @@ kotlin {
8787
implementation kotlin("stdlib-js")
8888
}
8989
}
90+
winMain {
91+
kotlin.srcDir('src/fallbackMain')
92+
}
93+
wasmMain {
94+
kotlin.srcDir('src/fallbackMain')
95+
}
96+
linArm32Main {
97+
kotlin.srcDir('src/fallbackMain')
98+
}
99+
linMips32Main {
100+
kotlin.srcDir('src/fallbackMain')
101+
}
102+
linMipsel32Main {
103+
kotlin.srcDir('src/fallbackMain')
104+
}
105+
lin64Main {
106+
kotlin.srcDir('src/fallbackMain')
107+
}
90108

91109
iosSimMain.dependsOn iosMain
92110
iosSimTest.dependsOn iosTest

lib/src/commonMain/kotlin/org/reduxkotlin/CreateStore.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.reduxkotlin
22

3+
import org.reduxkotlin.utils.getThreadName
34
import org.reduxkotlin.utils.isPlainObject
45

56
/**
@@ -44,6 +45,15 @@ fun <State> createStore(
4445
var currentListeners = mutableListOf<() -> Unit>()
4546
var nextListeners = currentListeners
4647
var isDispatching = false
48+
val storeThreadName = getThreadName()
49+
fun isSameThread() = getThreadName() == storeThreadName
50+
fun checkSameThread() = check(isSameThread()) {
51+
"""You may not call the store from a thread other than the thread on which it was created.
52+
|This includes: getState(), dispatch(), subscribe(), and replaceReducer()
53+
|This store was created on: '$storeThreadName' and current
54+
|thread is '${getThreadName()}'
55+
""".trimMargin()
56+
}
4757

4858
/**
4959
* This makes a shallow copy of currentListeners so we can use
@@ -64,6 +74,7 @@ fun <State> createStore(
6474
* @returns {S} The current state tree of your application.
6575
*/
6676
fun getState(): State {
77+
checkSameThread()
6778
check(!isDispatching) {
6879
"""|You may not call store.getState() while the reducer is executing.
6980
|The reducer has already received the state as an argument.
@@ -100,6 +111,7 @@ fun <State> createStore(
100111
* @returns {StoreSubscription} A fun to remove this change listener.
101112
*/
102113
fun subscribe(listener: StoreSubscriber): StoreSubscription {
114+
checkSameThread()
103115
check(!isDispatching) {
104116
"""|You may not call store.subscribe() while the reducer is executing.
105117
|If you would like to be notified after the store has been updated,
@@ -159,6 +171,7 @@ fun <State> createStore(
159171
* return something else (for example, a Promise you can await).
160172
*/
161173
fun dispatch(action: Any): Any {
174+
checkSameThread()
162175
require(isPlainObject(action)) {
163176
"""Actions must be plain objects. Use custom middleware for async
164177
|actions.""".trimMargin()
@@ -193,6 +206,7 @@ fun <State> createStore(
193206
* @returns {void}
194207
*/
195208
fun replaceReducer(nextReducer: Reducer<State>) {
209+
checkSameThread()
196210
currentReducer = nextReducer
197211

198212
// This action has a similar effect to ActionTypes.INIT.
@@ -217,7 +231,7 @@ fun <State> createStore(
217231
// the initial state tree.
218232
dispatch(ActionTypes.INIT)
219233

220-
return object: Store<State> {
234+
return object : Store<State> {
221235
override val getState = ::getState
222236
override var dispatch: Dispatcher = ::dispatch
223237
override val subscribe = ::subscribe
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.reduxkotlin.utils
2+
3+
const val UNKNOWN_THREAD_NAME = "UNKNOWN_THREAD_NAME"
4+
5+
/**
6+
* Returns the name of the current thread.
7+
*/
8+
expect fun getThreadName(): String
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.reduxkotlin.utils
2+
3+
/**
4+
* Fallback for platforms that have not been implemented yet.
5+
* This will allow usage of ReduxKotlin, but not allow
6+
* thread enforcement.
7+
* Linux, Win, WASM
8+
*/
9+
actual fun getThreadName(): String = UNKNOWN_THREAD_NAME
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.reduxkotlin.utils
2+
import platform.Foundation.NSThread.Companion.currentThread
3+
4+
actual fun getThreadName(): String = currentThread.name ?: UNKNOWN_THREAD_NAME
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.reduxkotlin.utils
2+
3+
actual fun getThreadName() = "main"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.reduxkotlin.utils
2+
3+
actual fun getThreadName(): String = Thread.currentThread().name

0 commit comments

Comments
 (0)