-
ocelot-inc authoredocelot-inc authored
Package fiber
The fiber
package allows for creating, running and managing fibers.
A fiber is a set of instructions which are executed with cooperative
multitasking. Fibers managed by the fiber package are associated with
a user-supplied function called the fiber function.
A fiber has three possible states: running, suspended or dead.
When a fiber is created with fiber.create()
, it is running.
When a fiber yields control with fiber.sleep()
, it is suspended.
When a fiber ends (because the fiber function ends), it is dead.
All fibers are part of the fiber registry. This registry can be searched
(fiber.find()
) - via fiber id (fid), which is numeric.
A runaway fiber can be stopped with fiber_object:cancel()
. However,
fiber_object:cancel()
is advisory — it works only if the runaway fiber
calls fiber.testcancel()
once in a while. Most box.* functions, such
as box.space...delete()
or box.space...update()
, do call
fiber.testcancel()
but box.space...select{}
does not. In practice,
a runaway fiber can only become unresponsive if it does many computations
and does not check whether it's been canceled.
The other potential problem comes from fibers which never get scheduled,
because they are not subscribed to any events, or because no relevant
events occur. Such morphing fibers can be killed with fiber.cancel()
at any time, since fiber.cancel()
sends an asynchronous wakeup event
to the fiber, and fiber.testcancel()
is checked whenever such an event occurs.
Like all Lua objects, dead fibers are garbage collected. The garbage collector frees pool allocator memory owned by the fiber, resets all fiber data, and returns the fiber (now called a fiber carcass) to the fiber pool. The carcass can be reused when another fiber is created.
Example
Make the function which will be associated with the fiber. This function
contains an infinite loop ("while 0 == 0" is always true). Each iteration
of the loop adds 1 to a global variable named gvar, then goes to sleep for
2 seconds. The sleep causes an implicit fiber.yield()
.
tarantool> fiber = require('fiber')
tarantool> console = require('console'); console.delimiter('!')
tarantool> function function_x()
-> gvar = 0
-> while 0 == 0 do
-> gvar = gvar + 1
-> fiber.sleep(2)
-> end
-> end!
---
...
tarantool> console.delimiter('')!
Make a fiber, associate function_x with the fiber, and start function_x. It will immediately "detach" so it will be running independently of the caller.
tarantool> fiber_of_x = fiber.create(function_x)
---
...
Get the id of the fiber (fid), to be used in later displays.
tarantool> fid = fiber_of_x:id()
---
...
Pause for a while, while the detached function runs. Then ... Display the fiber id, the fiber status, and gvar (gvar will have gone up a bit depending how long the pause lasted). The status is suspended because the fiber spends almost all its time sleeping or yielding.
tarantool> print('#',fid,'. ',fiber_of_x:status(),'. gvar=',gvar)
# 102 . suspended . gvar= 399
---
...
Pause for a while, while the detached function runs. Then ... Cancel the fiber. Then, once again ... Display the fiber id, the fiber status, and gvar (gvar will have gone up a bit more depending how long the pause lasted). This time the status is dead because the cancel worked.
tarantool> fiber_of_x:cancel()
... fiber `lua' has been cancelled
... fiber `lua': exiting
---
...
tarantool> print('#',fid,'. ',fiber_of_x:status(),'. gvar=',gvar)
# 102 . dead . gvar= 421
---
...