Skip to content

Commit

Permalink
Make fiber sugar more useful.
Browse files Browse the repository at this point in the history
Changed syntax of fiber.new to pass a wrap function to the block.  Also
accepts a callback to be notified when the block finishes or errors out.
  • Loading branch information
creationix committed Jun 24, 2012
1 parent 43e6d4d commit 417f36f
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 25 deletions.
55 changes: 33 additions & 22 deletions examples/fs-coroutines.lua
@@ -1,47 +1,58 @@
local fs = require('fs')
local timer = require('timer')
local fiber = require('fiber')
local fiber = require 'fiber'

fiber.new(function (resume, wait)
local function logic(wrap)
-- Wrap some functions for sync-style calling
local sleep = wrap(require('timer').setTimeout)
-- Can wrap modules too
local fs = wrap(require('fs'), true) -- true means to auto-handle errors

print("opening...")
fs.open(__dirname .. "/../LICENSE.txt", "r", "0644", resume)
local err, fd = wait()

p("on_open", {err=err,fd=fd})
local fd = fs.open(__filename, "r", "0644")
p("on_open", {fd=fd})

print("fstatting...")
fs.fstat(fd, resume)
local err, stat = wait()
p("stat", {err=err,stat=stat})
local stat = fs.fstat(fd)
p("stat", {stat=stat})

print("reading...")
local offset = 0
repeat
fs.read(fd, offset, 72, resume)
local err, chunk, length = wait()
p("on_read", {err=err,chunk=chunk, offset=offset, length=length})
local chunk, length = fs.read(fd, offset, 40)
p("on_read", {chunk=chunk, offset=offset, length=length})
offset = offset + length
until length == 0

print("pausing...")
timer.setTimeout(400, resume)
wait()
sleep(1000)

print("closing...")
fs.close(fd, resume)
local err = wait()
p("on_close", {err=err})
fs.close(fd)
p("on_close", {})

return fd, stat, offset

end

print "Starting fiber."
fiber.new(logic, function (err, fd, stat, offset)
if err then
p("ERROR", err)
error(err)
else
p("SUCCESS", { fd = fd, stat = stat, offset = offset })
end
end)
print "started."

fiber.new(function (resume, wait)
print "Starting another fiber."
fiber.new(function (wrap)

local readdir = wrap(require('fs').readdir)
print("scanning directory...")
fs.readdir(".", resume)
local err, files = wait()
local err, files = readdir(".")
p("on_open", {err=err,files=files})

end)
print "started second."


77 changes: 74 additions & 3 deletions lib/luvit/fiber.lua
Expand Up @@ -17,11 +17,82 @@ limitations under the License.
--]]
local coroutine = require('coroutine')
local debug = require 'debug'
local fiber = {}
function fiber.new(fn)
local resume = coroutine.wrap(fn)
resume(resume, coroutine.yield)
function fiber.new(block, callback)
local paused
local co = coroutine.create(block)
local function formatError(err)
local stack = debug.traceback(co, tostring(err))
if type(err) == "table" then
err.message = stack
return err
end
return stack
end
local function check(success, ...)
if not success then
if callback then
return callback(formatError(...))
else
error(formatError(...))
end
end
if not paused then
return callback and callback(nil, ...)
end
paused = false
end
local function wait(fn, ...)
if type(fn) ~= "function" then
error("can only wait on functions")
end
local args = {...}
args[#args + 1] = function (...)
check(coroutine.resume(co, ...))
end
fn(unpack(args))
paused = true
return coroutine.yield()
end
local function wrap(fn, handleErrors)
if type(fn) == "table" then
return setmetatable({}, {
__index = function (table, key)
return fn[key] and wrap(fn[key], handleErrors)
end
})
end
if type(fn) ~= "function" then
error("Can only wrap functions or tables of functions")
end
-- Do a simple curry for the passthrough wait wrapper
if not handleErrors then
return function (...)
return wait(fn, ...)
end
end
-- Or magically pull out the error argument and throw it if it's there.
-- Return all other values if no error.
return function (...)
local result = {wait(fn, ...)}
local err = result[1]
if err then error(err) end
return unpack(result, 2)
end
end
check(coroutine.resume(co, wrap, wait))
end
return fiber
Expand Down

0 comments on commit 417f36f

Please sign in to comment.