• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

effil/effil: Multithreading support for Lua

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

effil/effil

开源软件地址(OpenSource Url):

https://github.com/effil/effil

开源编程语言(OpenSource Language):

C++ 59.6%

开源软件介绍(OpenSource Introduction):

Effil

Generic badge Generic badge Generic badge Generic badge

LuaRocks license Join the chat at https://gitter.im/effil-chat/Lobby

Linux + MacOS Windows
build & run tests Build status

Effil is a multithreading library for Lua. It allows to spawn native threads and safe data exchange. Effil has been designed to provide clear and simple API for lua developers.

Effil supports lua 5.1, 5.2, 5.3 and LuaJIT. Requires C++14 compiler compliance. Tested with GCC 4.9+, clang 3.8 and Visual Studio 2015.

Table Of Contents

How to install

Build from src on Linux and Mac

  1. git clone --recursive https://github.com/effil/effil effil
  2. cd effil && mkdir build && cd build
  3. cmake .. && make install
  4. Copy effil.so to your project.

From lua rocks

luarocks install effil

Quick guide

As you may know there are not much script languages with real multithreading support (Lua/Python/Ruby and etc has global interpreter lock aka GIL). Effil solves this problem by running independent Lua VM instances in separate native threads and provides robust communicating primitives for creating threads and data sharing.

Effil library provides three major abstractions:

  1. effil.thread - provides API for threads management.
  2. effil.table - provides API for tables management. Tables can be shared between threads.
  3. effil.channel - provides First-In-First-Out container for sequential data exchange.

And bunch of utilities to handle threads and tables as well.

Examples

Spawn the thread

local effil = require("effil")

function bark(name)
    print(name .. " barks from another thread!")
end

-- run funtion bark in separate thread with name "Spaky"
local thr = effil.thread(bark)("Sparky")

-- wait for completion
thr:wait()

Output: Sparky barks from another thread!

Sharing data with effil.channel

local effil = require("effil")

-- channel allow to push data in one thread and pop in other
local channel = effil.channel()

-- writes some numbers to channel
local function producer(channel)
    for i = 1, 5 do
        print("push " .. i)
        channel:push(i)
    end
    channel:push(nil)
end

-- read numbers from channels
local function consumer(channel)
    local i = channel:pop()
    while i do
        print("pop " .. i)
        i = channel:pop()
    end
end

-- run producer
local thr = effil.thread(producer)(channel)

-- run consumer
consumer(channel)

thr:wait()

Output:

push 1
push 2
pop 1
pop 2
push 3
push 4
push 5
pop 3 
pop 4
pop 5

Sharing data with effil.table

effil = require("effil")

-- effil.table transfers data between threads
-- and behaves like regualr lua table
local storage = effil.table { string_field = "first value" }
storage.numeric_field = 100500
storage.function_field = function(a, b) return a + b end
storage.table_field = { fist = 1, second = 2 }

function check_shared_table(storage)
   print(storage.string_field)
   print(storage.numeric_field)
   print(storage.table_field.first)
   print(storage.table_field.second)
   return storage.function_field(1, 2)
end

local thr = effil.thread(check_shared_table)(storage)
local ret = thr:get()
print("Thread result: " .. ret)

Output:

first value
100500
1
2
Thread result: 3

Important notes

Effil allows to transmit data between threads (Lua interpreter states) using effil.channel, effil.table or directly as parameters of effil.thread.

  • Primitive types are transmitted 'as is' by copy: nil, boolean, number, string
  • Functions are dumped using lua_dump. Upvalues are captured according to the rules.
    • C functions (for which lua_iscfunction returns true) are transmitted just by a pointer using lua_tocfunction (in original lua_State) and lua_pushcfunction (in new lua_State).
      caution: in LuaJIT standard functions like tonumber are not real C function, so lua_iscfunction returns true but lua_tocfunction returns nullptr. Due to that we don't find way to transmit it between lua_States.
  • Userdata and Lua threads (coroutines) are not supported.
  • Tables are serialized to effil.table recursively. So, any Lua table becomes effil.table. Table serialization may take a lot of time for big table. Thus, it's better to put data directly to effil.table avoiding a table serialization. Let's consider 2 examples:
-- Example #1
t = {}
for i = 1, 100 do
   t[i] = i
end
shared_table = effil.table(t)

-- Example #2
t = effil.table()
for i = 1, 100 do
   t[i] = i
end

In the example #1 we create regular table, fill it and convert it to effil.table. In this case Effil needs to go through all table fields one more time. Another way is example #2 where we firstly created effil.table and after that we put data directly to effil.table. The 2nd way pretty much faster try to follow this principle.

Blocking and nonblocking operations:

All operations which use time metrics can be blocking or non blocking and use following API: (time, metric) where metric is time interval like 's' (seconds) and time is a number of intervals.

Example:

  • thread:get() - infinitely wait for thread completion.
  • thread:get(0) - non blocking get, just check is thread finished and return
  • thread:get(50, "ms") - blocking wait for 50 milliseconds.

List of available time intervals:

  • ms - milliseconds;
  • s - seconds (default);
  • m - minutes;
  • h - hours.

All blocking operations (even in non blocking mode) are interruption points. Thread hanged in such operation can be interrupted by invoking thread:cancel() method.

Example

local effil = require "effil"

local worker = effil.thread(function()
    effil.sleep(999) -- worker will hang for 999 seconds
end)()

worker:cancel(1) -- returns true, cause blocking operation was interrupted and thread was canceled

Function's upvalues

Working with functions Effil serializes and deserializes them using lua_dump and lua_load methods. All function's upvalues are stored following the same rules as usual. If function has upvalue of unsupported type this function cannot be transmitted to Effil. You will get error in that case.

Working with function Effil can store function environment (_ENV) as well. Considering environment as a regular table Effil will store it in the same way as any other table. But it does not make sence to store global _G, so there are some specific:

  • Lua = 5.1: function environment is not stored at all (due to limitations of lua_setfenv we cannot use userdata)
  • Lua > 5.1: Effil serialize and store function environment only if it's not equal to global environment (_ENV ~= _G).

API Reference

Thread

effil.thread is the way to create a thread. Threads can be stopped, paused, resumed and canceled. All operation with threads can be synchronous (with optional timeout) or asynchronous. Each thread runs with its own lua state.

Use effil.table and effil.channel to transmit data over threads. See example of thread usage here.

runner = effil.thread(func)

Creates thread runner. Runner spawns new thread for each invocation.

input: func - Lua function

output: runner - thread runner object to configure and run a new thread

Thread runner

Allows to configure and run a new thread.

thread = runner(...)

Run captured function with specified arguments in separate thread and returns thread handle.

input: Any number of arguments required by captured function.

output: Thread handle object.

runner.path

Is a Lua package.path value for new state. Default value inherits package.path form parent state.

runner.cpath

Is a Lua package.cpath value for new state. Default value inherits package.cpath form parent state.

runner.step

Number of lua instructions lua between cancelation points (where thread can be stopped or paused). Default value is 200. If this values is 0 then thread uses only explicit cancelation points.

Thread handle

Thread handle provides API for interaction with thread.

status, err, stacktrace = thread:status()

Returns thread status.

output:

  • status - string values describes status of thread. Possible values are: "running", "paused", "canceled", "completed" and "failed".
  • err - error message, if any. This value is specified only if thread status == "failed".
  • stacktrace - stacktrace of failed thread. This value is specified only if thread status == "failed".

... = thread:get(time, metric)

Waits for thread completion and returns function result or nothing in case of error.

input: Operation timeout in terms of time metrics

output: Results of captured function invocation or nothing in case of error.

thread:wait(time, metric)

Waits for thread completion and returns thread status.

input: Operation timeout in terms of time metrics

output: Returns status of thread. The output is the same as thread:status()

thread:cancel(time, metric)

Interrupts thread execution. Once this function was invoked 'cancellation' flag is set and thread can be stopped sometime in the future (even after this function call done). To be sure that thread is stopped invoke this function with infinite timeout. Cancellation of finished thread will do nothing and return true.

input: Operation timeout in terms of time metrics

output: Returns true if thread was stopped or false.

thread:pause(time, metric)

Pauses thread. Once this function was invoked 'pause' flag is set and thread can be paused sometime in the future (even after this function call done). To be sure that thread is paused invoke this function with infinite timeout.

input: Operation timeout in terms of time metrics

output: Returns true if thread was paused or false. If the thread is completed function will return false

thread:resume()

Resumes paused thread. Function resumes thread immediately if it was paused. This function does nothing for completed thread. Function has no input and output parameters.

Thread helpers

id = effil.thread_id()

Gives unique identifier.

output: returns unique string id for current thread.

effil.yield()

Explicit cancellation point. Function checks cancellation or pausing flags of current thread and if it's required it performs corresponding actions (cancel or pause thread).

effil.sleep(time, metric)

Suspend current thread.

input: time metrics arguments.

effil.hardware_threads()

Returns the number of concurrent threads supported by implementation. Basically forwards value from std:


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
sniperHW/luawrapper: a wrapper for lua api,writting by c++发布时间:2022-08-16
下一篇:
davidsiaw/luacppinterface: A Simple C++ Interface to Lua发布时间:2022-08-16
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap