Skip to the content.

# coro-channel

Documentation for the coro-channel module, version 3.0.3.

coro-channel is a wrapper module that wraps stream handles (or any other handle that inherits stream) to provide a sync style read/write interface making use of Lua coroutines, and without blocking the event loop.

# Installation

lit install creationix/coro-channel

On Lit search.


# Functions


# wrapRead(socket)

Wraps a stream handle for coroutine based reading.

This method does not require running in a coroutine

# Parameters

Param Type Description
socket uv_stream_t The stream handle to be wrapped for reading.

# Returns

Return Type Description
reader function See reader.
closer function see closer.

# Examples

  • A standard input echo example
    (Type something in the console to get an echo)
local uv = require("uv") -- or "luv" outside of Luvi
local wrapRead = require("coro-channel").wrapRead

local stdin = uv.new_tty(0, true) -- create a readable stream, a TTY stream in this case
local reader, closer = wrapRead(stdin) -- wrap the readable stream
for chunk in reader do -- each time something is typed in console: execute reader and store the result in chunk
  if chunk == '\n' then break end -- if the input is a new line (Enter) stop reading 
  print("You typed: " .. chunk) -- output the user input
end
closer() -- close the handle if reading has stopped
print("You have exited")

Note: In the above example we won’t receive an End of Stream (EoS), so we break manually on new lines, but this is not always the case, for example the coro-net reader will return nil when an EoS is received which would automatically break out of the for loop.


# wrapWrite(socket)

Wraps a stream handle for coroutine based writing.

# Parameters

Param Type Description
socket uv_stream_t The stream handle to be wrapped for writing.

# Returns

Return Type Description
writer function See writer.
closer function see closer.

# Examples

  • Timer writing to stdout TTY every second
local uv = require("uv") -- or "luv" outside of Luvi
local wrapWrite = require("coro-channel").wrapWrite

local stdout = uv.new_tty(1, false) -- create a writable stdout stream
local writer, closer = wrapWrite(stdout) -- wrap the writable stream

local passed = 0 -- how many second have passed?
local function callback() -- a callback to be executed every second
  coroutine.wrap(function() -- a new coroutine each time
    passed = passed + 1
    writer(passed .. " seconds has passed!\n") -- write to stdout
  end)()
end

writer("Hello to the timer! To exit press Ctrl + C\n")
local timer = uv.new_timer() -- create a timer handle
timer:start(1000, 1000, callback) -- start it and repeat every second

In the previous example the callback can also be created in the following way to avoid creating new coroutine each time:

local callback = coroutine.wrap(function()
  while true do
    passed = passed + 1
    writer(passed .. " seconds has passed!\n")
    coroutine.yield()
  end
end)

# wrapStream(socket)

Wraps a stream handle for both writing and reading.

Has the same effect to calling wrapRead and wrapWrite on the handle, such as:

local reader = wrapRead(stream)
local writer, closer = wrapWrite(stream)

# Parameters

Param Type Description
socket uv_stream_t The stream handle to be wrapped for reading and writing.

# Returns

Return Type Description
reader function See reader.
writer function See writer.
closer function see closer.

# Returned Functions


# reader()

Yields the running coroutine and resumes it after receiving a chunk of data. Effectively: wait until there is data to read, then return the read data.

# Returns

Return Type Description Provided On
data string / boolean Returns the chunk of data as string on success, otherwise false. Always
error string A string containing the error code caused the failure. Failure Only

# Examples

  • Using a reader in an iterator
local uv = require("uv") -- or "luv" outside of luvi
local wrapRead = require("coro-channel").wrapRead

local handle = uv.new_tty(1, true) -- or any kind of streams
local reader = wrapRead(handle)

for chunk, err in reader do
  if err then
    print("An error has occurred: " .. err)
    break
  elseif chunk == '\n' then -- press Enter to exit the tty
    break
  end
  print("Data chunk successfully read: " .. chunk)
end

print("Reading data done")

# writer(chunk)

Yields the running coroutine and resumes it when done writing the provided chunk of data. Effectively: write the data and wait until it has been written, then return.

# Parameters

Param Type Description
chunk string / table / nil nil indicates End of Stream and will completely close the stream if there’s nothing to read, if there is, it will only shutdown the writing duplex.
If the provided value is a string it will be written to the stream as a single chunk.
If a table of strings is provided the string values will be concatenated and written in a single system call.

# Returns

Return Type Description Provided On
success boolean Whether or not the operation was successful. Always
error string A string containing the error code caused this failure Failure Only

# Examples

Assuming the following example of using writer with a TTY handle running in a coroutine:

local uv = require("uv") -- or "luv" outside of Luvi
local wrapWrite = require("coro-channel").wrapWrite

local handle = uv.new_tty(0, false) -- or any kind of streams
local writer = wrapWrite(handle)

local tbl = {"Hello ", "There", "!!", "\n"}
local success, err = writer(tbl)
if not success then
  print("An error has occurred: " .. err)
  return
end

Note: Usually though, this example is not how you use it with a TTY handle, the example is simplified and for explanation purposes only.


# closer()

Closes the wrapped stream handle if it hasn’t been already closed. You cannot read or write from/to a closed stream.

Note: This call returns immediately, even if the stream hasn’t fully closed yet.