Skip to content

Commit

Permalink
feat: add a new module channel similar to Golang's channel
Browse files Browse the repository at this point in the history
See example `examples/channel.lua`.

Signed-off-by: Jianhui Zhao <[email protected]>
  • Loading branch information
zhaojh329 committed Nov 9, 2024
1 parent 65befb7 commit 61fd15a
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ install(

install(
FILES time.lua sys.lua file.lua dns.lua socket.lua packet.lua mqtt.lua
websocket.lua sync.lua nl.lua genl.lua ip.lua nl80211.lua
websocket.lua sync.lua channel.lua nl.lua genl.lua ip.lua nl80211.lua
DESTINATION ${LUA_INSTALL_PREFIX}/eco
)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Lua-eco also provides some modules for you to build applications quickly:
* `dns`: Provides a DNS client implementation for Lua-eco applications, allowing you to perform DNS lookups and resolve domain names.
* `ubus`: Provides a Lua interface to the [ubus] system in OpenWrt, allowing you to interact with system services and daemons.
* `sync`: Provides operations for synchronization between coroutines.
* `channel`: Provides a mechanism for communication between coroutines by sending and receiving values.
* `netlink`: Provides operations for inter-process communication (IPC) between both the kernel and userspace processes.
* `nl80211`: Show/manipulate wireless devices and their configuration.
* `termios`: Bind unix API for terminal/serial I/O.
Expand Down
1 change: 1 addition & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Lua-eco 还提供了一些有用的模块,方便您快速构建应用程序:
* `dns`: 为 Lua-eco 应用程序提供了一个 DNS 客户端实现,允许您执行 DNS 查找和解析域名。
* `ubus`: 提供了一个 Lua 接口,用于 OpenWrt 中的 [ubus] 系统,允许您与系统服务和守护程序交互。
* `sync`: 提供了协程间同步的操作。
* `channel`: 提供了一种协程间通信的机制,通过发送和接收数据。
* `netlink`: 为内核和用户空间进程之间的进程间通信(IPC)提供操作。
* `nl80211`: 显示/操作无线设备及其配置。
* `termios`: 绑定 unix 接口用于操作终端和串口。
Expand Down
110 changes: 110 additions & 0 deletions channel.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
-- SPDX-License-Identifier: MIT
-- Author: Jianhui Zhao <[email protected]>

local sync = require 'eco.sync'

local M = {}

local methods = {}

function methods:length()
return #self.buf
end

function methods:close()
if self.closed then
return
end

self.closed = true
self.cond_recv:signal()
end

-- On success, true is returned
-- On error, false is returned with a string describing the error
function methods:send(v, timeout)
assert(not self.closed, 'sending on closed channel')

local buf = self.buf

if #buf == self.capacity then
local ok, err = self.cond_send:wait(timeout)
if not ok then
return false, err
end
end

buf[#buf + 1] = v

self.cond_recv:signal()

return true
end

-- On success, the value received is returned
-- On closed, nil is returned
-- On error, nil is returned with a string describing the error
function methods:recv(timeout)
local buf = self.buf

if #buf < 1 then
if self.closed then
return nil
end

local ok, err = self.cond_recv:wait(timeout)
if not ok then
return nil, err
end
end

if #buf < 1 then
return nil
end

local v = buf[1]

table.remove(buf, 1)

self.cond_send:signal()

return v
end

local metatable = {
__index = methods
}

--[[
A channel provides a mechanism for communication between coroutines by sending and receiving values.
When a coroutine sends data to a channel, if the channel is full, the sending operation is blocked
until another coroutine has taken data from the channel.
Similarly, when a coroutine receives data from a channel, if there is no data available in the channel,
the receiving operation will be blocked until another coroutine has sent data to the channel.
Closing a channel notifies other coroutines that the channel is no longer in use. After a channel is closed,
other coroutines can still receive data from it, but they can no longer send data to it.
A channel can have a buffer, default is 1.
--]]
function M.new(capacity)
assert(capacity == nil or math.type(capacity) == 'integer', 'capacity must be an integer')

if not capacity or capacity < 1 then
capacity = 1
end

local ch = {
cond_send = sync.cond(),
cond_recv = sync.cond(),
capacity = capacity,
closed = false,
buf = {}
}

return setmetatable(ch, metatable)
end

return M
41 changes: 41 additions & 0 deletions examples/channel.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env eco

local channel = require 'eco.channel'
local time = require 'eco.time'

-- Create a channel with 5 buffers.
local ch = channel.new(5)

eco.run(function()
local i = 1
while true do
ch:send(i)

print(os.time(), 'send', i)

if i == 5 then
ch:close()
break
end

i = i + 1
time.sleep(1)
end
end)

eco.run(function()
time.sleep(2)
while true do
local v = ch:recv()
if v then
print(os.time(), 'recv:', v)
else
print(os.time(), 'closed')
break
end
end
end)

while true do
time.sleep(1)
end

0 comments on commit 61fd15a

Please sign in to comment.