0

Working in lua, I have a table of key/value pairs

local fmtsToRun = { 
  name = function() 
    return configSubTable
  end
}

Which could be 1 or more entries in length. I need to loop through each entry and run a subprocess (through some libuv C bindings).

With a normal for loop, the subprocess results from libuv return after the loop has finished running, leading to things showing up out of order. The result would be

  • Loop Start
  • Loop Entry 1
  • Job 1 starts
  • Loop Entry 2
  • Job 2 starts
  • Loop Ends
  • Job1 Returns
  • Job2 Returns

What I need to have is

  • Loop Start
  • Loop Entry 1
  • Job 1 starts
  • Job1 Returns
  • Loop Entry 2
  • Job 2 starts
  • Job2 Returns
  • Loop Ends

I've also tried writing my own version of pairs() to and using something like coroutines to handle the callbacks


for fmt, output in jobIterator(fmtsToRun) do
  print('finished running', output)
end


  local function jobIterator(tbl)
    return coroutine.wrap(function()
      local fmtConf, fmtName
      fmtName, fmtConf = next(tbl, fmtName)
      if nil~=fmtConf then
          local conf = fmtConf()
          local output = nil
          -- wrapper util from Libuv library
          local job = Job:new({
              cmd = conf.cmd,
              args = conf.args,
              on_stdout = onStdout, -- process output
              on_stderr = onStderr, -- process any error
              on_exit = function()
                coroutine.yield(fmtName, output)
              end
          })
          job.send(conf.data)
      end
    end)
  end

which leads to this error messages.

attempt to yield across C-call boundary

What would be the "right" way to wait for the job to finish and continue the loop while maintaining the correct order?

mhartington
  • 6,905
  • 4
  • 40
  • 75
  • I'd just use os.execute() instead of coroutine. That way you're guaranteed that the current process will finish in the desired order before starting a new task. – Doyousketch2 Oct 30 '20 at 04:40

1 Answers1

0

A better option was to manually control the flow and recursively call a function to step through the table one entry at a time.

local runner = {}
for _, output in pairs(fmtsToRun) do
  table.insert(runner, output)
end
jobIterator(runner)

local function jobIterator(tbl)
  local F = {}
  function F.step()
    if #tbl == 0 then
      return
    end
    local current = table.remove(tbl, 1)
    F.run(current)
  end
  
  function F.run(conf)
    local output = nil
    -- wrapper util from Libuv library
    local job =
      Job:new(
      {
        cmd = conf.cmd,
        args = conf.args,
        on_stdout = onStdout, -- process output
        on_stderr = onStderr, -- process any error
        on_exit = function()
          -- do what you need with output
          print(output)
          F.step()
        end
      }
    )
    job.send(conf.data)
  end

  F.step()
end

mhartington
  • 6,905
  • 4
  • 40
  • 75