Функция criticalSection() для выполнения произвольного кода в потокобезопасном режиме

Страницы: 1
RSS
Функция criticalSection() для выполнения произвольного кода в потокобезопасном режиме
 
Пока в соседней ветке пытаюсь выяснить, как использовать 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)()
 
В примере, конечно, не
Код
t1.insert(v1)
а
Код
table.insert(t1, v1)
Но вы это поняли...
Страницы: 1
Читают тему
Наверх