Improve documentation for fiber-scoped variables
* Especially around Enumerator.
This commit is contained in:
parent
39e70eef72
commit
4495dea153
Notes:
git
2022-12-20 22:06:15 +00:00
4
NEWS.md
4
NEWS.md
@ -103,8 +103,8 @@ Note: We're only listing outstanding class updates.
|
|||||||
fiber. [[Feature #19078]]
|
fiber. [[Feature #19078]]
|
||||||
|
|
||||||
Existing Thread and Fiber local variables can be tricky to use.
|
Existing Thread and Fiber local variables can be tricky to use.
|
||||||
Thread local variables are shared between all fibers, making it
|
Thread-local variables are shared between all fibers, making it
|
||||||
hard to isolate, while Fiber local variables can be hard to
|
hard to isolate, while Fiber-local variables can be hard to
|
||||||
share. It is often desirable to define unit of execution
|
share. It is often desirable to define unit of execution
|
||||||
("execution context") such that some state is shared between all
|
("execution context") such that some state is shared between all
|
||||||
fibers and threads created in that context. This is what Fiber
|
fibers and threads created in that context. This is what Fiber
|
||||||
|
4
cont.c
4
cont.c
@ -2153,7 +2153,7 @@ rb_fiber_storage_set(VALUE self, VALUE value)
|
|||||||
/**
|
/**
|
||||||
* call-seq: Fiber[key] -> value
|
* call-seq: Fiber[key] -> value
|
||||||
*
|
*
|
||||||
* Returns the value of the fiber-local variable identified by +key+.
|
* Returns the value of the fiber-scoped variable identified by +key+.
|
||||||
*
|
*
|
||||||
* The +key+ must be a symbol, and the value is set by Fiber#[]= or
|
* The +key+ must be a symbol, and the value is set by Fiber#[]= or
|
||||||
* Fiber#store.
|
* Fiber#store.
|
||||||
@ -2176,7 +2176,7 @@ rb_fiber_storage_aref(VALUE class, VALUE key)
|
|||||||
/**
|
/**
|
||||||
* call-seq: Fiber[key] = value
|
* call-seq: Fiber[key] = value
|
||||||
*
|
*
|
||||||
* Assign +value+ to the fiber-local variable identified by +key+.
|
* Assign +value+ to the fiber-scoped variable identified by +key+.
|
||||||
* The variable is created if it doesn't exist.
|
* The variable is created if it doesn't exist.
|
||||||
*
|
*
|
||||||
* +key+ must be a Symbol, otherwise a TypeError is raised.
|
* +key+ must be a Symbol, otherwise a TypeError is raised.
|
||||||
|
45
enumerator.c
45
enumerator.c
@ -73,6 +73,8 @@
|
|||||||
* puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
|
* puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
|
||||||
* # => ["0:foo", "1:bar", "2:baz"]
|
* # => ["0:foo", "1:bar", "2:baz"]
|
||||||
*
|
*
|
||||||
|
* == External Iteration
|
||||||
|
*
|
||||||
* An Enumerator can also be used as an external iterator.
|
* An Enumerator can also be used as an external iterator.
|
||||||
* For example, Enumerator#next returns the next value of the iterator
|
* For example, Enumerator#next returns the next value of the iterator
|
||||||
* or raises StopIteration if the Enumerator is at the end.
|
* or raises StopIteration if the Enumerator is at the end.
|
||||||
@ -83,15 +85,44 @@
|
|||||||
* puts e.next # => 3
|
* puts e.next # => 3
|
||||||
* puts e.next # raises StopIteration
|
* puts e.next # raises StopIteration
|
||||||
*
|
*
|
||||||
* Note that enumeration sequence by +next+, +next_values+, +peek+ and
|
* +next+, +next_values+, +peek+ and +peek_values+ are the only methods
|
||||||
* +peek_values+ do not affect other non-external
|
* which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+).
|
||||||
* enumeration methods, unless the underlying iteration method itself has
|
|
||||||
* side-effect, e.g. IO#each_line.
|
|
||||||
*
|
*
|
||||||
* Moreover, implementation typically uses fibers so performance could be
|
* These methods do not affect other internal enumeration methods,
|
||||||
* slower and exception stacktraces different than expected.
|
* unless the underlying iteration method itself has side-effect, e.g. IO#each_line.
|
||||||
*
|
*
|
||||||
* You can use this to implement an internal iterator as follows:
|
* External iteration differs *significantly* from internal iteration
|
||||||
|
* due to using a Fiber:
|
||||||
|
* - The Fiber adds some overhead compared to internal enumeration.
|
||||||
|
* - The stacktrace will only include the stack from the Enumerator, not above.
|
||||||
|
* - Fiber-local variables are *not* inherited inside the Enumerator Fiber,
|
||||||
|
* which instead starts with no Fiber-local variables.
|
||||||
|
* - Fiber-scoped variables *are* inherited and are designed
|
||||||
|
* to handle Enumerator Fibers. Assigning to a Fiber-scope variable
|
||||||
|
* only affects the current Fiber, so if you want to change state
|
||||||
|
* in the caller Fiber of the Enumerator Fiber, you need to use an
|
||||||
|
* extra indirection (e.g., use some object in the Fiber-scoped
|
||||||
|
* variable and mutate some ivar of it).
|
||||||
|
*
|
||||||
|
* Concretely:
|
||||||
|
* Thread.current[:fiber_local] = 1
|
||||||
|
* Fiber[:scoped_var] = 1
|
||||||
|
* e = Enumerator.new do |y|
|
||||||
|
* p Thread.current[:fiber_local] # for external iteration: nil, for internal iteration: 1
|
||||||
|
* p Fiber[:scoped_var] # => 1, inherited
|
||||||
|
* Fiber[:scoped_var] += 1
|
||||||
|
* y << 42
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* p e.next # => 42
|
||||||
|
* p Fiber[:scoped_var] # => 1 (it ran in a different Fiber)
|
||||||
|
*
|
||||||
|
* e.each { p _1 }
|
||||||
|
* p Fiber[:scoped_var] # => 2 (it ran in the same Fiber/"stack" as the current Fiber)
|
||||||
|
*
|
||||||
|
* == Convert External Iteration to Internal Iteration
|
||||||
|
*
|
||||||
|
* You can use an external iterator to implement an internal iterator as follows:
|
||||||
*
|
*
|
||||||
* def ext_each(e)
|
* def ext_each(e)
|
||||||
* while true
|
* while true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user