You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* simplify instrumentation by making a single insertion point source instead of having one in every class
* handle a double-wrapping case which leads to errors; allow agent to choose how to handle it
* support more commonly used operators (such as `scan`, `buffer`, `debounce` with dynamic timeout)
Unfortunately, this change doesn't cover all possible scenarios of using flows, as many of them interoperate with `Channel`s, and it should be addressed separately.
Copy file name to clipboardExpand all lines: IntelliJ-patches.md
+17-3Lines changed: 17 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,15 +61,29 @@ The agent needs three entities to establish a proper asynchronous stack traces c
61
61
62
62
The key for MutableStateFlow is the element itself. For MutableSharedFlow, the element is wrapped into a unique object to prevent bridging mistakes when two equal elements are emitted from different places.
63
63
64
-
Also, operators `debounce` and `sample`are supported. As they use an intermediary flow inside, the transferred values are wrapped and unwrapped the same way as in MutableSharedFlow.
64
+
Most of the operators applicable to flows (such as `map`, `scan`, `debounce`, `buffer`) are supported. As some of them use an intermediary flow inside, the transferred values are wrapped and unwrapped the same way as in MutableSharedFlow.
65
65
It means there may be all-library async stack traces between a stack trace containing `emit` and a stack trace containing `collect`.
66
66
67
+
There is no support yet for many operators that heavily use `Channel`s inside (such as `timeout`), as well as for functions that convert flows to channels and vice versa (such as `produceIn`).
68
+
67
69
### API
68
70
69
-
No new public methods are introduced; some logic was extracted to separate methods so that the debugger agent could instrument it properly:
71
+
Some logic related to instrumentation was extracted to separate methods so that the debugger agent could instrument it properly:
70
72
71
73
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternal` -- wrapper class used to create a unique object for the debugger agent
72
74
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternalKt.wrapInternal` -- returns passed argument by default; the agent instruments it to call `wrapInternalDebuggerCapture` instead
73
-
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternalKt.wrapInternalDebuggerCapture` -- wraps passed arguments into a `FlowValueWrapperInternal`; only used after transformation
75
+
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternalKt.wrapInternalDebuggerCaptureX` -- wraps passed arguments into a `FlowValueWrapperInternal`; only used after transformation.
76
+
`X` may mean `Strict` or `Lenient`. Both methods handle double-wrapping, which is always an error that is hard to investigate if it arises naturally.
77
+
-`Strict` throws an exception, thus allowing fail-fast strategy
78
+
-`Lenient` returns its argument without wrapping it again
79
+
Debugger agent decides which version to use based on IDE settings.
74
80
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternalKt.unwrapInternal` -- returns passed argument by default; the agent instruments it to call `unwrapInternalDebuggerCapture` instead
75
81
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternalKt.unwrapInternalDebuggerCapture` -- unwraps passed argument so it returns the original value; only used after transformation
82
+
-`kotlinx.coroutines.flow.internal.FlowValueWrapperInternalKt.emitInternal(FlowCollector, value)` -- common insertion point for a debugger agent; simplifies instrumentation; the value is always being unwrapped inside
83
+
84
+
One internal method was added to `BufferedChannelIterator`: `nextInternal` -- same as `next` but may return a wrapped value. It should only be used with a function that is capable of unwrapping the value (see `BufferedChannel.emitAll` and `BufferedChannelIterator.next`), so there's a guarantee a wrapped value will always unwrap before emitting.
85
+
86
+
Why not just let `next` return a maybe wrapped value? That's because it is heavily used outside a currently supported scope. For example, one may just indirectly call it from a for-loop. In this case, unwrapping will never happen, and a user will get a handful of `ClassCastException`s.
87
+
88
+
One public method was added to support `buffer` and operators that use it inside:
89
+
-`ReceiveChannel.emitAll`. It encapsulates emitting values in `FlowCollector.emitAllImpl` and has a special implementation in `BufferedChannel`.
0 commit comments