Cosock
Cosock is a coroutine runtime written in pure Lua and based on the popular luasocket library.
The goal of the project is to provide the same interfaces that luasocket provides but wrapped up in coroutines to allow for concurrent IO.
Note: these docs will use the term coroutine, task, and thread interchangeably to all mean a lua coroutine
For example, the following 2 lua programs use luasocket to define a tcp client and server.
--client.lua
local socket = require "socket"
local client = socket.tcp()
client:connect("0.0.0.0", 9999)
while true do
print("sending ping")
client:send("ping\n")
local response = assert(client:receive())
assert(response == "pong")
end
--server.lua
local socket = require "socket"
local server = socket.tcp()
server:bind("0.0.0.0", 9999)
server:listen()
print("listening", server:getsockname())
local client = server:accept()
while true do
local request = assert(client:receive())
assert(request == "ping")
print("sending pong")
client:send("pong\n")
end
If you were to run lua ./server.lua
first and then run lua ./client.lua
you should see each terminal print out
their "sending ..." messages forever.
Using cosock, we can actually write the same thing as a single application.
-- client_server.lua
local cosock = require "cosock"
local socket = require "cosock.socket"
local ip = "0.0.0.0"
local server = socket.tcp()
--- Since the client and server are in the same application
--- we can use an OS assigned port and share it across the
--- two tasks, to coordinate the two tasks to start in the order
--- we want, we can use a cosock channel to make sure both tasks
--- have the same port number
local port_tx, port_rx = cosock.channel.new()
--- Spawn a task for handling the server side of the socket
cosock.spawn(function()
server:bind(ip, 0)
local _ip, p = server:getsockname()
port_tx:send(p)
server:listen()
local client = server:accept()
while true do
local request = assert(client:receive())
print(string.format("received %q", request))
assert(request == "ping")
print("sending pong")
client:send("pong\n")
end
end, "server task")
--- Spawn a task for handling the client side of the socket
cosock.spawn(function()
--- wait for the server to be ready.
local port = assert(port_rx:receive())
local client = socket.tcp()
client:connect(ip, port)
while true do
print("sending ping")
client:send("ping\n")
local request = assert(client:receive())
assert(request == "pong")
end
end, "client task")
--- Finally we tell cosock to run our 2 coroutines until they are done
--- which should be forever
cosock.run()
Now if we run this with lua ./client_server.lua
we should see the messages alternate.
Notice that we called cosock.spawn
twice, once for the server task and
once for the client task, we are going to dig into that next. We also added a call to cosock.run
at the bottom of our example, this function will run our tasks until there is no more work to do
so it is important you don't forget it or nothing will happen.