Модуль fiber
С помощью модуля fiber можно:
- Создавать, запускать и управлять файберами.
- Отправлять и получать сообщения между различными процессами (то есть разными подключениями, сессиями или файберами) через каналы.
- Использовать механизм синхронизации для файберов, аналогичный «переменным условия» и функциям операционной системы, таким
как
pthread_cond_wait()иpthread_cond_signal().
Ниже приведен перечень всех функций и элементов модуля fiber.
Name | Use |
|---|---|
Fibers | |
Создание и запуск файбера | |
Создание файбера без запуска | |
Получение объекта файбера | |
Получение объекта файбера по ID | |
Перевод файбера в спящий режим | |
Передача управления | |
Получение статуса текущего файбера | |
Получение информации обо всех файберах | |
Возврат таблицы активных файберов и отображение потребления CPU | |
Отмена файбера | |
Проверка того, отменен ли текущий файбер | |
Установка максимального кванта времени по умолчанию для всех файберов | |
Установка кванта времени для выполнения текущего файбера | |
Продление кванта времени для выполнения текущего файбера | |
Проверка того, истек ли квант времени для текущего файбера | |
Получение системного времени в секундах | |
Получение системного времени в микросекундах | |
Получение монотонного времени в секундах | |
Получение монотонного времени в микросекундах | Fiber object |
Получение ID файбера | |
Получение имени файбера | |
Задание имени файбера | |
Получение статуса файбера | |
Отмена файбера | |
Установка максимального кванта времени для файбера | |
Локальное хранилище внутри файбера | |
Разрешение новому файберу присоединиться (join) | |
Ожидание перехода состояния файбера в 'dead' | |
Channels | |
Создание канала связи | |
Отправка сообщения через канал | |
Закрытие канала | |
Получение сообщения из канала | |
Проверка того, пуст ли канал | |
Подсчет сообщений в канале | |
Проверка того, заполнен ли канал | |
Проверка того, есть ли ожидающие читатели у пустого канала | |
Проверка того, есть ли ожидающие писатели у заполненного канала | |
Проверка того, закрыт ли канал | |
Полезный пример использования каналов | Condition variables |
Создание переменной состояния | |
Перевод файбера в спящий режим до пробуждения другим файбером | |
Пробуждение одного файбера | |
Пробуждение всех файберов | |
Полезный пример использования переменных состояния |
Файбер — это набор инструкций, выполняемых в режиме
кооперативной многозадачности. Модуль fiber позволяет
создать файбер и связать его с пользовательской
функцией, называемой функцией файбера.
Файбер может находиться в одном из следующих состояний: running,
suspended, ready или dead. В программе с файберами в любой момент
времени выполняется только один из них. Выполняемый файбер
приостанавливает свою работу только при явной
передаче управления другому файберу, готовому к
выполнению.
Когда функция файбера завершается, файбер завершает работу и переходит в
состояние dead. При необходимости можно
отменить выполнение запущенного или
приостановленного файбера. Еще одна полезная возможность —
ограничение времени выполнения файбера
для длительных операций.
Чтобы создать файбер, вызовите одну из следующих функций:
- fiber.create() — создает файбер и немедленно
запускает его. Начальное состояние файбера —
running. - fiber.new() — создает файбер, но не запускает его.
Начальное состояние файбера —
ready. Такие файберы можно присоединить с помощью функции fiber_object:join() и получить результат, возвращенный функцией файбера.
Передача управления (yield) — это действие в
кооперативной среде, при котором управление потоком передается от
текущего файбера другому файберу, готовому к выполнению. Модуль fiber
предоставляет следующие функции для явной передачи управления другому
файберу:
-
fiber.yield() — передает управление планировщику.
-
fiber.sleep() — передает управление планировщику и приостанавливает выполнение на указанное количество секунд.
Чтобы отменить файбер, используйте функцию fiber_object.cancel. Также можно вызвать fiber.kill(), чтобы найти файбер по его числовому идентификатору и отменить его.
Если файбер работает слишком долго без передачи управления, можно
использовать квант времени файбера (fiber slice) для ограничения времени
его выполнения. Параметр compat
fiber_slice_default управляет значением по
умолчанию для максимального кванта времени файбера.
Существует два типа квантов: предупреждающий и ошибочный.
-
При превышении предупреждающего кванта в журнал записывается предупреждение, например:
fiber has not yielded for more than 0.500 seconds -
При превышении ошибочного кванта файбер отменяется, и вызывается ошибка
FiberSliceIsExceeded:
FiberSliceIsExceeded: fiber slice is exceeded
Управление передается другому файберу, готовому к выполнению.
Проверка кванта времени выполняется всеми функциями, работающими со спейсами и индексами, такими как index_object.select(), space_object.replace() и так далее. Также можно использовать функцию fiber.check_slice() в коде приложения для проверки, не превышен ли квант для текущего файбера.
Следующие функции переопределяют значение по умолчанию для максимального кванта времени файбера:
- fiber.set_max_slice(slice) — задает максимальный квант по умолчанию для всех файберов.
- fiber_object:set_max_slice(slice) —
задает максимальный квант для конкретного файбера. Максимальный квант
устанавливается при пробуждении файбера. Это может быть первый запуск
или пробуждение после
fiber.yield().
Изменить или увеличить квант для текущего выполняемого файбера можно с помощью следующих функций:
- fiber.set_slice(slice) — задает квант для текущего выполнения файбера.
- fiber.extend_slice(slice) — увеличивает квант
для текущего выполнения файбера. Учтите, что указанные значения не
влияют на выполнение файбера после
fiber.yield().
Чтобы получить информацию обо всех файберах или о конкретном файбере, используйте следующие функции:
- fiber.info — возвращает информацию обо всех файберах.
- fiber.status() — получает статус текущего файбера. Чтобы получить статус указанного файбера, вызовите fiber_object:status().
- fiber.top() — показывает все активные файберы и потребление ими CPU.
Сборщик мусора собирает недоступные файберы так же, как и все Lua-объекты: сборщик мусора в Lua освобождает память выделенного для файбера пула, сбрасывает все данные файбера и возвращает файбер (который теперь называется каркасом файбера) в пул файберов. Каркас можно использовать повторно при создании другого файбера.
Файбер обладает всеми возможностями Lua корутины (coroutine), и все принципы программирования, применимые к корутинам Lua, применимы и к файберам. Однако в Tarantool внесены улучшения для файберов, и они используются внутри системы. Поэтому, хотя использование корутин возможно и поддерживается, рекомендуется использовать файберы.
Создание и запуск файбера. Происходит создание файбера, который незамедлительно начинает работу.
Параметры:
function(function) — функция, связываемая с файберомfunction-arguments— аргументы, передаваемые функции
Возвращает
созданный объект файбера
Тип возвращаемого значения
userdata
Пример:
В примере ниже показано, как создать файбер с помощью fiber.create:
-- app.lua --fiber = require('fiber')function greet(name)print('Hello, '..name)endgreet_fiber = fiber.create(greet, 'John')print('Fiber already started')
После запуска app.lua должен
отобразиться следующий вывод:
$ tarantool app.luaHello, JohnFiber already started
Создает файбер, но не запускает его. Созданный файбер запускается после
того, как создатель файбера (то есть задача, вызывающая fiber.new())
передаст управление. Начальное состояние файбера — ready.
Файберы, созданные с помощью fiber.new, можно присоединить, вызвав
функцию fiber_object:join(), и получить результат,
возвращенный функцией файбера. Чтобы присоединить файбер, необходимо
сделать его присоединяемым с помощью
fiber_object:set_joinable().
Параметры:
function(function) — функция, связываемая с файберомfunction-arguments— аргументы, передаваемые функции
Возвращает
созданный объект файбера
Тип возвращаемого значения
userdata
Пример:
В примере ниже показано, как создать файбер с помощью fiber.new:
-- app.lua --fiber = require('fiber')function greet(name)print('Hello, '..name)endgreet_fiber = fiber.new(greet, 'John')print('Fiber not started yet')
После запуска app.lua должен
отобразиться следующий вывод:
$ tarantool app.luaFiber not started yetHello, John
Возвращает
объект файбера для текущего запланированного файбера.
Тип возвращаемого значения
userdata
Пример:
tarantool> fiber.self()---- status: runningname: interactiveid: 101
Параметры:
id(number) — числовой идентификатор файбера.
Возвращает
объект файбера для указанного файбера.
Тип возвращаемого значения
userdata
Пример:
tarantool> fiber.find(101)---- status: runningname: interactiveid: 101
Передача управления планировщику и переход в режим ожидания на указанное количество секунд. Только текущий файбер можно перевести в режим ожидания.
Параметры:
time(number) — количество секунд ожидания.
Возможные ошибки:
см. Пример ошибки при передаче управления.
Пример:
Приведенная ниже функция increment содержит бесконечный цикл, который
увеличивает глобальную переменную counter на 1. Затем текущий файбер
переходит в режим ожидания на period секунд. sleep вызывает неявный
fiber.yield().
-- app.lua --fiber = require('fiber')counter = 0function increment(period)while true docounter = counter + 1fiber.sleep(period)endendincrement_fiber = fiber.create(increment, 2)require('console').start()
После запуска скрипта выведите информацию о файбере: ID файбера, его статус и значение счетчика:
tarantool> print('ID: ' .. increment_fiber:id() .. '\nStatus: ' .. increment_fiber:status() .. '\nCounter: ' .. counter)ID: 104Status: suspendedCounter: 8---
Затем отмените файбер и выведите информацию о файбере еще раз. В этот
раз статус файбера — dead.
tarantool> increment_fiber:cancel()---tarantool> print('ID: ' .. increment_fiber:id() .. 'nStatus: ' .. increment_fiber:status() .. 'nCounter: ' .. counter)ID: 104 Status: deadCounter: 12---
Передача управления планировщику. Работает аналогично fiber.sleep(0).
Возможные ошибки:
см. Пример ошибки при вызове yield.
Пример:
В приведенном ниже примере два файбера связаны с одной и той же функцией. Каждый файбер передает управление после вывода приветствия.
-- app.lua --fiber = require('fiber')function greet()while true doprint('Enter a name:')name = io.read()print('Hello, '..name..'. I am fiber '..fiber.id())fiber.yield()endendfor i = 1, 2 dofiber_object = fiber.create(greet)fiber_object:cancel()end
Вывод может выглядеть следующим образом:
$ tarantool app.luaEnter a name:JohnHello, John. I am fiber 104Enter a name:JaneHello, Jane. I am fiber 105
Возвращает статус текущего файбера. Если передан fiber_object,
возвращает статус указанного файбера.
Параметры:
fiber_object(fiber_object) — (необязательный) объект файбера
Возвращает
статус fiber. Одно из значений: dead, suspended или running.
Тип возвращаемого значения
string
Пример:
tarantool> fiber.status()---- running
Возврат информации о всех файберах.
Параметры:
backtrace(boolean) — показывать стек вызовов. По умолчанию:true. Установите значениеfalse, чтобы отображать меньше информации (разрешение символов может быть ресурсоемким).bt(boolean) — то же, что иbacktrace, но с более низким приоритетом.
Возвращает
количество переключений контекста (csw), стек вызовов, общая память,
используемая память, ID файбера (fid), имя файбера. Если включен
fiber.top или Tarantool собран с ENABLE_FIBER_TOP, также возвращается
процессорное время (time).
Тип возвращаемого значения
table
Описание возвращаемых значений
csw– количество переключений контекста.backtrace,bt– стек вызовов каждого файбера, показывающий, где он был создан и какие функции были вызваны.memory:total– общий объем памяти, занимаемый файбером как C-структурой, его стеком и т.д.used– фактический объем памяти, используемый файбером.
time– дублирует значение "time" из fiber.top().cpu для каждого файбера. Обратите внимание, что подсчетtimeведется, только если активна функцияfiber.top().
Пример:
tarantool> fiber.info({ bt = true })---- 101:csw: 1backtrace:- C: '#0 0x5dd130 in lbox_fiber_id+96'- C: '#1 0x5dd13d in lbox_fiber_stall+13'- L: stall in =[C] at line -1- L: (unnamed) in \@builtin/fiber.lua at line 59- C: '#2 0x66371b in lj_BC_FUNCC+52'- C: '#3 0x628f28 in lua_pcall+120'- C: '#4 0x5e22a8 in luaT_call+24'- C: '#5 0x5dd1a9 in lua_fiber_run_f+89'- C: '#6 0x45b011 in fiber_cxx_invoke(int (*)(__va_list_tag*), __va_list_tag*)+17'- C: '#7 0x5ff3c0 in fiber_loop+48'- C: '#8 0x81ecf4 in coro_init+68'memory: {total = 516472, used = 0}time: 0name: luafid: 101102:csw: 0backtrace:- C: '#0 (nil) in +63'- C: '#1 (nil) in +63'memory: {total = 516472, used = 0}time: 0name: on_shutdownfid: 102...
Отображение всех активных файберов и потребляемых ими ресурсов ЦП.
Возвращает
таблица с двумя записями: cpu и cpu_misses
cpu – это ещё одна таблица, в которой ключами являются строки с ID и
именами файберов. Для каждого файбера доступны 3 метрики:
instant(в процентах) — доля времени, в течение которой файбер выполнялся на предыдущей итерации цикла обработки событий.average(в процентах) — экспоненциальное скользящее среднее значенийinstantпо всем предыдущим итерациям цикла обработки событий.time(в секундах) — оценка времени ЦП, затраченного каждым файбером на обработку за все время его существования. Записьtimeтакже добавляется к выводу информации о файбере с помощьюfiber.info()(дублируется записьtimeизfiber.top().cpuдля каждого файбера). Обратите внимание, что подсчетtimeведется, только если активна функцияfiber.top().
cpu_misses показывает число раз, когда поток TX регистрировал перенос
файбера на другое ядро процессора во время последней итерации цикла
обработки событий. fiber.top() использует счетчик меток времени для
измерения времени выполнения каждого файбера. Однако в разных ядрах
могут быть разные значения счетчика, поэтому полагаться на разность
показаний счетчика можно только в том случае, если оба измерения были
проведены на одном и том же ядре; в противном случае разность показаний
может даже быть отрицательной. Когда поток TX переносится на другое ядро
процессора, Tarantool просто предполагает, что разность показаний была
нулевой для последнего измерения. Это снижает точность вычислений,
поэтому чем больше значение cpu_misses, тем ниже точность результатов
fiber.top().
Пример:
tarantool> fiber.top()---- cpu:107/lua: {instant = 30.967324490456, time = 0.351821993, average = 25.582738345233}104/lua: {instant = 9.6473633128437, time = 0.110869897, average = 7.9693406131877}101/on_shutdown: {instant = 0, time = 0, average = 0}103/lua: {instant = 9.8026528631511, time = 0.112641118, average = 18.138387232255}106/lua: {instant = 20.071174377224, time = 0.226901357, average = 17.077908441831}102/interactive: {instant = 0, time = 9.6858e-05, average = 0}105/lua: {instant = 9.2461986412164, time = 0.10657528, average = 7.7068458630827}1/sched: {instant = 20.265286315108, time = 0.237095335, average = 23.141537169257}cpu_misses: 0...
Обратите внимание, что по умолчанию новые файберы, созданные с помощью fiber.create, называются 'lua', поэтому лучше задавать им явные имена с помощью fiber_object:name('name').
В выводе fiber.top() есть несколько системных файберов, которые могут
быть полезны:
-
sched— это специальный системный файбер. Он планирует задачи для других файберов, если они есть, а также обрабатывает некоторые событияlibev. Высокие значенияinstantиaverageв выводеfiber.top()для него возможны в двух случаях:- На экземпляре практически нет нагрузки — тогда выполняется почти
только
sched, а остальные файберы находятся в состоянии ожидания. Поэтому относительно других файберов нагрузкаschedможет составлять почти 100%. schedобрабатывает большое количество системных событий. Это не должно вызывать проблем с производительностью.
- На экземпляре практически нет нагрузки — тогда выполняется почти
только
-
Файберы
mainобрабатывают запросы, поступающие по сети (iproto-запросы). Таких файберов несколько, и при необходимости создаются новые. При поступлении нового запроса свободный файбер принимает его и выполняет. Запросом может быть типичныйselect/replace/delete/insertили вызов функции. Например, conn:eval() или conn:call().
Находит файбер по его числовому идентификатору и отменяет его. Иными словами, fiber.kill() объединяет fiber.find() и fiber_object:cancel().
Параметры:
id(number) — идентификатор файбера, который нужно отменить.
Возможные ошибки:
указанный файбер не существует или отмена не разрешена.
Пример:
tarantool> fiber.kill(fiber.id()) -- функция с self может вызвать окончание программы---- error: fiber is cancelled
Проверка отмены действующего файбера и выдача исключения, если файбер отменен.
Пример:
tarantool> fiber.testcancel()---- error: fiber is cancelled
Устанавливает максимальный квант времени по умолчанию для всех файберов. Квант времени файбера ограничивает период выполнения файбера без передачи управления.
Параметры:
-
slice(number/table) — квант времени файбера, который может принимать одно из следующих значений: -
период времени (в секундах), задающий квант ошибки. Пример:
fiber.set_max_slice(3). -
таблица, задающая кванты предупреждения и ошибки (в секундах). Пример:
fiber.set_max_slice({warn = 1.5, err = 3}).
Пример:
В примере ниже показано, как использовать set_max_slice для
ограничения кванта времени для всех файберов.
fiber.check_slice() вызывается внутри длительной
операции, чтобы определить, истёк ли квант времени текущего файбера.
-- app.lua --fiber = require('fiber')clock = require('clock')fiber.set_max_slice({warn = 1.5, err = 3})time = clock.monotonic()function long_operation()while clock.monotonic() - time < 5 dofiber.check_slice()-- Long-running operation ⌛⌛⌛ --endendlong_operation_fiber = fiber.create(long_operation)
Вывод должен выглядеть следующим образом:
$ tarantool app.luafiber has not yielded for more than 1.500 secondsFiberSliceIsExceeded: fiber slice is exceeded
Устанавливает квант времени для текущего файбера. Квант времени файбера ограничивает период выполнения файбера без передачи управления.
Параметры:
-
slice(number/table) — квант времени файбера, который может принимать одно из следующих значений: -
период времени (в секундах), задающий квант ошибки. Пример:
fiber.set_slice(3). -
таблица, задающая кванты предупреждения и ошибки (в секундах). Пример:
fiber.set_slice({warn = 1.5, err = 3}).
Пример:
В примере ниже показано, как использовать set_slice для ограничения
кванта времени текущего файбера.
fiber.check_slice() вызывается внутри длительной
операции, чтобы определить, истёк ли квант времени текущего файбера.
-- app.lua --fiber = require('fiber')clock = require('clock')time = clock.monotonic()function long_operation()fiber.set_slice({warn = 1.5, err = 3})while clock.monotonic() - time < 5 dofiber.check_slice()-- Long-running operation ⌛⌛⌛ --endendlong_operation_fiber = fiber.create(long_operation)
Вывод должен выглядеть следующим образом.
$ tarantool app.luafiber has not yielded for more than 1.500 secondsFiberSliceIsExceeded: fiber slice is exceeded
Продлевает квант времени для текущего
файбера. Например, если максимальный квант ошибки по умолчанию
установлен с помощью fiber.set_max_slice() в
значение 3 секунды, extend_slice(1) продлевает квант ошибки до 4
секунд.
Параметры:
-
slice(number/table) — квант времени файбера, который может принимать одно из следующих значений: -
период времени (в секундах), задающий квант ошибки. Пример:
fiber.extend_slice(1). -
таблица, задающая кванты предупреждения и ошибки (в секундах). Пример:
fiber.extend_slice({warn = 0.5, err = 1}).
Пример:
В примере ниже показано, как использовать extend_slice для продления
кванта времени текущего файбера. Максимальный квант времени по умолчанию
устанавливается с помощью set_max_slice.
-- app.lua --fiber = require('fiber')clock = require('clock')fiber.set_max_slice({warn = 1.5, err = 3})time = clock.monotonic()function long_operation()fiber.extend_slice({warn = 0.5, err = 1})while clock.monotonic() - time < 5 dofiber.check_slice()-- Long-running operation ⌛⌛⌛ --endendlong_operation_fiber = fiber.create(long_operation)
Вывод должен выглядеть следующим образом.
$ tarantool app.luafiber has not yielded for more than 2.000 secondsFiberSliceIsExceeded: fiber slice is exceeded
FiberSliceIsExceeded возникает через 4 секунды.
Проверяет, истёк ли квант времени текущего файбера. Квант времени файбера ограничивает период выполнения файбера без передачи управления.
Пример:
См. примеры для следующих функций:
Возвращает
текущее системное время (в секундах с начала эпохи) в виде числа Lua. Время берётся из часов цикла обработки событий, что делает этот вызов очень быстрым, но всё же полезным для формирования искусственных ключей кортежей.
Тип возвращаемого значения
number
Пример:
tarantool> fiber.time(), fiber.time()---- 1448466279.2415- 1448466279.2415
Возвращает
текущее системное время (в микросекундах с начала эпохи) в виде 64-битного целого числа. Время берется из часов цикла обработки событий.
Тип возвращаемого значения
cdata (ctype<int64_t>)
Пример:
tarantool> fiber.time(), fiber.time64()---- 1448466351.2708- 1448466351270762
Получение монотонного времени в секундах. Для вычисления времени ожидания лучше
использовать fiber.clock(), поскольку fiber.time()
сообщает системное время, а оно может меняться при изменениях в системе.
Возвращает
число с плавающей запятой в секундах, представляющее прошедшее время с некоторого момента в прошлом, которое гарантированно не изменится в течение жизни процесса.
Тип возвращаемого значения
number
Пример:
tarantool> start = fiber.clock()---tarantool> print(start)248700.58805---tarantool> print(fiber.time(), fiber.time()-start)1600785979.8291 1600537279.241---
То же, что и fiber.clock(), но в микросекундах.
Возвращает
количество секунд в виде 64-битного целого числа, представляющее прошедшее время по настенным часам с некоторого момента в прошлом, который гарантированно не изменяется в течение жизни процесса.
Тип возвращаемого значения
cdata (ctype<int64_t>)
Параметры:
fiber_object— как правило, это объект, на который ссылается возвращаемое значение fiber.create или fiber.self или fiber.find
Возвращает
ID файбера.
Тип возвращаемого значения
number
fiber.self():id() может также быть выражен как fiber.id().
Пример:
tarantool> fiber_object = fiber.self()---tarantool> fiber_object:id()---- 101
Параметры:
fiber_object— как правило, это объект, на который ссылается возвращаемое значение fiber.create или fiber.self или fiber.find
Возвращает
имя файбера.
Тип возвращаемого значения
string
fiber.self():name() может также быть выражен как fiber.name().
Пример:
tarantool> fiber.self():name()---- interactive
Изменение имени файбера. По умолчанию файбер интерактивного режима сервера Tarantool называется 'interactive', а новые файберы, созданные с помощью fiber.create, называются 'lua'. Задание файберам отдельных имен упрощает их различение при использовании fiber.info и fiber.top(). Максимальная длина имени — 255.
Параметры:
fiber_object— как правило, это объект, на который ссылается возвращаемое значение fiber.create или fiber.self или fiber.findname(string) — новое имя файбера.options:truncate=true– усекает имя до максимальной длины, если оно слишком длинное. Если этот параметр равенfalse(по умолчанию),fiber.name(new_name)выдает исключение, если новое имя слишком длинное. Максимальная длина имени —255(начиная с версии 2.4.1).
Возвращает
nil
Пример:
tarantool> fiber.self():name('non-interactive')---
Возврат статуса указанного файбера.
Параметры:
fiber_object— как правило, это объект, на который ссылается возвращаемое значение fiber.create или fiber.self или fiber.find
Возвращает
статус файбера. Одно из значений: «dead», «suspended» или «running».
Тип возвращаемого значения
string
fiber.self():status() можно также записать как fiber.status().
Пример:
tarantool> fiber.self():status()---- running
Отправка запроса на отмену файбера. Работающие и приостановленные
файберы можно отменить. После отмены файбера попытки работать с ним
приводят к ошибкам, например, fiber_object:name()
вызывает error: the fiber is dead. Но завершенный файбер все еще может
сообщить свой ID и статус.
Отмена файбера происходит асинхронно. Чтобы дождаться окончания отмены,
используйте fiber_object:join(). После вызова
fiber_object:cancel() файбер может проверить, был ли он удален. Если он
этого не сделает, его отменить невозможно.
Параметры:
fiber_object— как правило, это объект, на который ссылается возвращаемое значение fiber.create или fiber.self или fiber.find
Возвращает
nil
Возможные ошибки:
нельзя отменить указанный объект файбера.
Пример:
См. пример для fiber.sleep().
Устанавливает максимальный квант времени для файбера. Квант времени файбера ограничивает период выполнения файбера без передачи управления.
Параметры:
-
slice(number/table) — квант времени файбера, который может принимать одно из следующих значений:- период времени (в секундах), задающий квант ошибки. Пример:
long_operation_fiber.set_max_slice(3). - таблица, задающая кванты предупреждения и ошибки (в секундах).
Пример:
long_operation_fiber.set_max_slice({warn = 1.5, err = 3}).
- период времени (в секундах), задающий квант ошибки. Пример:
Пример:
В примере ниже показано, как использовать set_max_slice для
ограничения кванта времени файбера. fiber.check_slice()
вызывается внутри длительной операции, чтобы определить, истёк ли квант
времени файбера.
-- app.lua --fiber = require('fiber')clock = require('clock')time = clock.monotonic()function long_operation()while clock.monotonic() - time < 5 dofiber.check_slice()-- Long-running operation ⌛⌛⌛ --endendlong_operation_fiber = fiber.new(long_operation)long_operation_fiber:set_max_slice({warn = 1.5, err = 3})
Вывод должен выглядеть следующим образом.
$ tarantool app.luafiber has not yielded for more than 1.500 secondsFiberSliceIsExceeded: fiber slice is exceeded
Локальное хранилище внутри файбера. Это Lua-таблица, которая создается
при первом обращении. Хранилище может содержать любое количество
именованных значений с учетом ограничений памяти. Именование может
выполняться с помощью {fiber_object}.storage.{name} или
{fiber_object}.storage['{name}'] или с помощью числа
{fiber_object}.storage[{number}]. Значениями могут быть числа или
строки.
fiber.storage уничтожается при завершении файбера, независимо от
того, как он завершен — через {fiber_object}:cancel() или с помощью
return в функции файбера. Более того, хранилище очищается даже для
файберов из пула, обслуживающих запросы IProto. Файберы из пула никогда
по-настоящему не завершаются, но их хранилище очищается после каждого
запроса. Это позволяет использовать fiber.storage как полноценное
хранилище в рамках запроса. Такое поведение реализовано в версиях
2.2.3, 2.3.2, 2.4.1 и всех последующих.
Хранилище может быть создано для файбера независимо от того, как создан
сам файбер — из C или из Lua. Например, файбер можно создать в C с
помощью fiber_new(), затем он может выполнять вставку в спейс, в
котором есть Lua-триггеры on_replace, и один из триггеров может
создать fiber.storage. Это хранилище удаляется при остановке файбера.
Пример:
В примере ниже показано, как сохранить последнее введенное имя в хранилище файбера и получить это значение перед отменой файбера.
-- app.lua --fiber = require('fiber')function greet()while true doprint('Enter a name:')name = io.read()if name ~= 'bye' thenfiber.self().storage.name = nameprint('Hello, ' .. name)elseprint('Goodbye, ' .. fiber.self().storage['name'])fiber.self():cancel()endendendfiber_object = fiber.create(greet)
Вывод может выглядеть следующим образом:
$ tarantool app.luaEnter a name:JohnHello, JohnEnter a name:JaneHello, JaneEnter a name:byeGoodbye, Jane
См. также box.session.storage.
Делает файбер присоединяемым. Присоединяемый файбер можно ожидать с помощью fiber_object:join().
Лучше всего вызывать fiber_object:set_joinable() до начала выполнения
функции файбера, так как в противном случае файбер может перейти в
состояние dead до того, как fiber_object:set_joinable() вступит в
силу. Обычная последовательность действий:
- Вызовите
fiber.new()вместоfiber.create(), чтобы создать новый fiber_object. Не передавайте управление, поскольку это приведет к началу работы функции файбера. - Вызовите
fiber_object:set_joinable(true), чтобы сделать новый fiber_object присоединяемым. Теперь можно передать управление. - Вызовите
fiber_object:join(). Как правило, следует вызватьfiber_object:join(), в противном случае статус файбера может перейти в 'suspended' (приостановлен) после выполнения функции, а не 'dead' (завершен).
Параметры:
is_joinable(boolean) — логическое значение, указывающее, является ли файбер присоединяемым
Возвращает
nil
Пример:
См. пример для fiber_object:join().
Присоединение к файберу. Присоединение позволяет получить результат, возвращенный функцией файбера.
Присоединение запускает функцию файбера и ожидает перехода статуса
файбера в dead. Обычно статус становится dead по завершении
выполнения функции. Присоединение вызывает передачу управления,
поэтому, если файбер находится в состоянии suspended, выполнение его
функции возобновляется.
Обратите внимание, что присоединение работает только если файбер создан с помощью fiber.new() и сделан присоединяемым с помощью fiber_object:set_joinable().
Параметры:
timeout(number) — максимальное количество секунд ожидания завершения файбера. По умолчанию: бесконечность.
Возвращает
Метод join возвращает два значения:
- Логическое значение, которое показывает, успешно ли завершилась функция файбера.
- Возвращаемое значение функции файбера.
Если первое значение — false, значит функция файбера завершилась
аварийно, а второй результат содержит сведения об ошибке, которые можно
распаковать так же, как результат pcall.
Тип возвращаемого значения
boolean + result type, or boolean + struct error
Возможные ошибки:
файбер уже присоединен параллельным вызовом fiber:join().
Пример:
В примере ниже показано, как получить результат, возвращенный функцией файбера.
fiber = require('fiber')function add(a, b)return a + bendadd_fiber = fiber.new(add, 5, 6)add_fiber:set_joinable(true)is_success, result = add_fiber:join()print('Is successful: '.. tostring(is_success))print('Returned value: '..result)
Вывод должен выглядеть следующим образом.
$ tarantool app.luaIs successful: trueReturned value: 11
Предупреждение: функция yield() и любая функция, которая неявно передает управление (например, sleep()), может упасть (выдать исключение).
Например, в этой функции есть цикл, выполняющийся до вызова
cancel(). Последнее, что будет выведено —
'before yield', что демонстрирует ошибку при выполнении yield():
цикл не продолжился до следующей проверки
testcancel().
fiber = require('fiber')function function_name()while true doprint('before testcancel')fiber.testcancel()print('before yield')fiber.yield()endendfiber_object = fiber.create(function_name)fiber.sleep(.1)fiber_object:cancel()
Вызов fiber.channel() для создания и получение нового объекта канала.
Вызов других процедур по каналу для отправки сообщений, получения сообщений или проверки статуса канала.
Обмен сообщениями является синхронным. Сборщик мусора Lua помечает или
освобождает канал, когда он больше не используется, как и любой другой
объект Lua. Используйте объектно-ориентированный синтаксис, например,
channel:put(message) вместо fiber.channel.put(message).
Создание нового канала связи.
Параметры:
capacity(int) — максимальное количество слотов (мест для сообщенийchannel:put), которые могут использоваться одновременно. Значение по умолчанию — 0.
Возвращает
новый объект канала. В выводе консоли он сериализуется как
channel: [number], где [number] — возвращаемое значение
channel_object:count().
Тип возвращаемого значения
userdata
Отправка сообщения по каналу связи. Если канал заполнен,
channel:put() ожидает, пока не освободится слот в канале.
Значение по умолчанию для емкости канала — 0. При
этом значении по умолчанию channel:put() ожидает бесконечно, пока не
будет вызван channel:get().
Параметры:
message(lua-value) — отправляемое значение, обычно строка, число или таблицаtimeout(number) — максимальное время ожидания освобождения слота в секундах. По умолчанию: бесконечность.
Возвращает
Если указан timeout и в течение указанного времени в канале нет
свободного слота, возвращается false. Если канал закрыт, возвращается
false. В остальных случаях возвращается true, что означает успешное
выполнение.
Тип возвращаемого значения
boolean
Закрытие канала. Все, кто находится в режиме ожидания в канале,
отключаются. Все последующие операции channel:get() вернут нулевое
значение nil, а все последующие операции channel:put() вернут
false (ложь).
Перехват и удаление сообщения из канала. Если канал пуст,
channel:get() будет ожидать сообщения.
Параметры:
timeout(number) — максимальное время ожидания сообщения в секундах. По умолчанию: бесконечность.
Возвращает
Если указан timeout и в течение указанного времени в канале нет
сообщений, возвращается nil. Если канал закрыт, возвращается nil. В
остальных случаях возвращается сообщение, помещенное в канал с помощью
channel:put().
Тип возвращаемого значения
обычно строка, число или таблица, в зависимости от того, что было
передано в channel:put
Проверка пустоты канала (отсутствие сообщений).
Возвращает
true, если канал пуст. В противном случае — false.
Тип возвращаемого значения
boolean
Определение количества сообщений в канале.
Возвращает
количество сообщений.
Тип возвращаемого значения
number
Проверка заполненности канала.
Возвращает
true, если канал заполнен (количество сообщений в канале равно
количеству слотов, поэтому нет места для нового сообщения). В противном
случае — false.
Тип возвращаемого значения
boolean
Проверка пустого канала на наличие читателей в состоянии ожидания
сообщения после отправки запросов channel:get().
Возвращает
true, если есть ожидающие читатели. В противном случае — false.
Тип возвращаемого значения
boolean
Проверка полного канала на наличие писателей в состоянии ожидания
после отправки запросов channel:put().
Возвращает
true, если есть ожидающие писатели. В противном случае — false.
Тип возвращаемого значения
boolean
Проверка того, закрыт ли канал.
Возвращает
true, если канал уже закрыт. В противном случае — false.
Тип возвращаемого значения
boolean
В данном примере дается примерное представление о том, как должны выглядеть функции для файберов. Предполагается, что на функции ссылается fiber.create().
fiber = require('fiber')channel = fiber.channel(10)function consumer_fiber()while true dolocal task = channel:get()...endendfunction consumer2_fiber()while true do-- 10 secondslocal task = channel:get(10)if task ~= nil then...else-- timeoutendendendfunction producer_fiber()while true dotask = box.space...:select{...}...if channel:is_empty() then-- channel is emptyendif channel:is_full() then-- channel is fullend...if channel:has_readers() then-- there are some fibers-- that are waiting for dataend...if channel:has_writers() then-- there are some fibers-- that are waiting for readersendchannel:put(task)endendfunction producer2_fiber()while true dotask = box.space...select{...}-- 10 secondsif channel:put(task, 10) then...else-- timeoutendendend
Вызов fiber.cond() используется для создания именованной условной
переменной, которая будет называться 'cond' для примеров данного
раздела.
Вызов cond:wait() используется, чтобы заставить файбер ожидать сигнал,
с помощью условной переменной.
Вызов cond:signal() используется, чтобы отправить сигнал для
пробуждения отдельного файбера, который выполнил запрос cond:wait().
Вызов cond:broadcast() используется для отправки сигнала всем
файберам, которые выполнили cond:wait().
Создание новой условной переменной.
Возвращает
новая условная переменная.
Тип возвращаемого значения
объект Lua
Перевод файбера в режим ожидания до пробуждения другим файбером с
помощью метода signal() или broadcast(). Переход в режим ожидания
вызывает неявную передачу управления fiber.yield().
Параметры:
timeout(number) — количество секунд ожидания, по умолчанию = бесконечно.
Возвращает
Если задан timeout и сигнал не поступает в течение указанного времени
ожидания, wait() возвращает false. Если поступает сигнал или
широковещательное сообщение, wait() возвращает true.
Тип возвращаемого значения
boolean
Пробуждение отдельного файбера, который выполнил wait() для той же
переменной. Не выполняет передачу управления (yield).
Тип возвращаемого значения
nil
Пробуждение всех файберов, которые выполнили wait() для той же
переменной. Не выполняет передачу управления (yield).
Тип возвращаемого значения
nil
Предположим, что экземпляр Tarantool запущен и принимает подключения на localhost, порт 3301. Предположим, что гостевые пользователи имеют права на подключение. Для запуска двух клиентов будет использоваться утилита tt.
В первом терминале введите:
$ tt connect localhost:3301tarantool> fiber = require('fiber')tarantool> cond = fiber.cond()tarantool> cond:wait()
Задача повиснет, поскольку cond:wait() – без дополнительного
аргумента времени ожидания timeout – уйдет в режим ожидания до
изменения условной переменной.
Во втором терминале введите:
$ tt connect localhost:3301tarantool> cond:signal()
Теперь снова взгляните на терминал №1. Он покажет, что ожидание
прекратилось, и функция cond:wait() вернула значение true.
В данном примере показана зависимость от использования глобальной
условной переменной с произвольным именем cond. В реальной жизни
разработчики следят за использованием различных имен для условных
переменных в разных приложениях.