SkillAgentSearch skills...

Effil

Multithreading support for Lua

Install / Use

/learn @effil/Effil
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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

<details> <summary><b>Spawn the thread</b></summary> <p>
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!

</p> </details> <details> <summary><b>Sharing data with effil.channel</b></summary> <p>
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
</p> </details> <details> <summary><b>Sharing data with effil.table</b></summary> <p>
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
</p> </details>

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.

<details> <summary><b>Example</b></summary> <p>
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 cancelled
</p> </details>

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

View on GitHub
GitHub Stars455
CategoryCustomer
Updated1d ago
Forks46

Languages

C++

Security Score

100/100

Audited on Apr 2, 2026

No findings