Issue #400: Prevent side effects / glitches in Observable implementations.#453
Issue #400: Prevent side effects / glitches in Observable implementations.#453pocmo wants to merge 1 commit intomozilla-mobile:masterfrom
Conversation
| @PublishedApi internal val queue: MutableList<() -> Unit> = mutableListOf() | ||
| @PublishedApi internal var isProcessing = false | ||
|
|
||
| inline fun modifyWithoutSideEffects(crossinline action: () -> Unit): Unit = synchronized(lock) { |
There was a problem hiding this comment.
[ktlint] WARNING: [no-unit-return] Unnecessary "Unit" return type in column 74
Codecov Report
@@ Coverage Diff @@
## master #453 +/- ##
============================================
- Coverage 76.54% 75.65% -0.89%
+ Complexity 915 898 -17
============================================
Files 143 137 -6
Lines 3376 3282 -94
Branches 484 462 -22
============================================
- Hits 2584 2483 -101
- Misses 544 560 +16
+ Partials 248 239 -9
Continue to review full report at Codecov.
|
|
@csadilek What do you think? If you agree I'll go ahead with the patch and apply it to |
|
|
||
| queue.forEach { action -> | ||
| action() | ||
| } |
There was a problem hiding this comment.
This may cause a concurrent modification exception if we are adding something to the queue while processing the queue. I may add a test for that and fix it. We may want to use an actual queue here.
There was a problem hiding this comment.
hm yes, or we take the recursive approach: #400 (comment) ?
| } else { | ||
| isProcessing = true | ||
|
|
||
| action() |
There was a problem hiding this comment.
I'm afraid that a return statement inside the lambda will jump out of this method because it's getting inlined. I thought crossinline would prevent that. But I seem to be able to still use a return@label:
fun someMethod(bar: Foo?) = sideEffects.modifyWithoutSideEffects {
if (bar == null) {
// This doesn't compile because of 'crossinline'
return
}
if (bar == null) {
// This compiles... but isn't it the same as the one above?
return@modifyWithoutSideEffects
}
// ...
}
There was a problem hiding this comment.
I wonder if every action() should actually be wrapped in:
try {
action()
} finally {
processQueue()
}... so that we will always continue with the next action if one of them returns after being inlined?
There was a problem hiding this comment.
This try catch doesn't really work... Every action we process in the queue would need to be wrapped in the same try-catch. We can do that with recursion - but we can't inline a method that is called recursively..
There was a problem hiding this comment.
Interesting. Shouldn't we just use noinline for the lambda here? It shouldn't have the power to jump out of the executing method :).
There was a problem hiding this comment.
I wanted to inline aggressively so that using SideEffects has no penalty. Otherwise Java (6) will need to create an object (+ the class) for every lambda :(
|
@pocmo yes, I like the solution. This way the observable can "decide" whether or not it allows side-effects. So, let's get in hardened and landed ;). |
9bdd9ac to
ef57ba7
Compare
…le implementations.
ef57ba7 to
d89db2d
Compare
|
Updated the PR. This is working nicely with |
🔥 |
|
Closing: I moved this PR out of the sprint. I want to think a bit more about this. |
This is a first version of a helper to fix the issue described in #400.
It's not used in any
Observableyet. We may want to use it inSessionManagerandSessionbut probably notEngineSessionimplementations because observers do not really modify the internal state.