Пока в соседней ветке пытаюсь выяснить, как использовать table.ssort() по назначению, придумал ей применение во благо: функция criticalSection() ниже выполняет переданную ей функцию, предварительно заблокировав другой поток.
Важно: в своей функции, которую вы передаете в criticalSection(), ни в коем случае не вызывайте потокобезопасные функции, а используйте их стандартные версии. Ваша функция может "бросать исключения" - финальный вариант это поддерживает.
Сама функция ниже, пример использования:
Код
local t1, t2 = {}, {}
local function insert(v1, v2)
if v1 > v2 then error("v1 > v2") end
t1.insert(v1)
t2.insert(v2)
return v1 + v2, v1 - v2
end
-- далее где-то в разных потоках "атомарно" вставляем оба значения
local r1, r2 = criticalSection(insert, a1, a2)
Функция (вам нужны первые две строки и финальный (последний) вариант, остальные в целях объяснения):
Код
-- В Lua 5.1 нет table.unpack, но есть unpack.
if not table.unpack then table.unpack = unpack end
-- Понятная версия. На входе функция и ее аргументы (ноль или более),
-- переданная функция вызывается со своими аргументами под
-- блокировкой, на выходе все ее возвращаемые значения.
local function criticalSection(func, ...)
-- Запаковываем аргументы для переданной функции в table.
local args = {...}
local res
-- Функция "сравнения" для table.ssort() (передаваемые элементы
-- упорядочиваемого массива игнорируются).
local less = function()
-- Распаковываем аргументы обратно в список, вызываем с ними
-- переданную функцию, запаковываем результаты в table.
res = { func(table.unpack(args)) }
-- Говорим, что первый элемент упорядочиваемого массива меньше
-- второго, то есть менять элементы местами не нужно.
return true
end
-- "Упорядочиваем" массив из двух элементов (то есть наша функция
-- "сравнения" вызовется ровно один раз), при этом во время вызова
-- удерживается блокировка.
table.ssort({ 0, 0 }, less)
-- Распаковываем результаты обратно в список и возвращаем его.
return table.unpack(res)
end
-- Слегка оптимизированная версия: массив { 0, 0 } создается только один
-- раз, функции unpack и ssort ищутся в модуле table также только один раз.
local criticalSection = (function()
local unpack, ssort = table.unpack, table.ssort
local array = { 0, 0 }
return function(func, ...)
local args = {...}
local res
local less = function()
res = { func(unpack(args)) }
return true
end
ssort(array, less)
return unpack(res)
end
end)()
-- Финальная версия: если внутри функции, переданной пользователем,
-- возникнет ошибка, то table.ssort() вылетит, не сняв блокировку (по
-- крайней мере так происходит в моей версии 7.5.0.72 - знаю, что
-- старая, но других брокер не дает). Ниже код, решающий проблему -
-- на всякий случай именно его и следует использовать.
local criticalSection = (function()
local unpack, ssort = table.unpack, table.ssort
local array = { 0, 0 }
local function pack(ok, ...) return ok, {...} end
return function(func, ...)
local args = {...}
local ok, res
local less = function()
ok, res = pack(pcall(func, unpack(args)))
return true
end
ssort(array, less)
if ok then
return unpack(res)
else
error(unpack(res), 0)
end
end
end)()