{"id":13759997,"url":"https://github.com/effil/effil","last_synced_at":"2025-05-10T10:31:06.327Z","repository":{"id":16215638,"uuid":"79269639","full_name":"effil/effil","owner":"effil","description":"Multithreading support for Lua","archived":false,"fork":false,"pushed_at":"2023-07-02T14:54:40.000Z","size":384,"stargazers_count":439,"open_issues_count":18,"forks_count":47,"subscribers_count":27,"default_branch":"master","last_synced_at":"2024-11-16T17:40:57.383Z","etag":null,"topics":["cpp","lua","multiple-threads","thread","thread-library"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/effil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-01-17T20:39:22.000Z","updated_at":"2024-11-14T23:16:29.000Z","dependencies_parsed_at":"2024-01-15T03:42:45.903Z","dependency_job_id":null,"html_url":"https://github.com/effil/effil","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effil%2Feffil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effil%2Feffil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effil%2Feffil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effil%2Feffil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/effil","download_url":"https://codeload.github.com/effil/effil/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253401041,"owners_count":21902595,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","lua","multiple-threads","thread","thread-library"],"created_at":"2024-08-03T13:01:02.006Z","updated_at":"2025-05-10T10:31:05.982Z","avatar_url":"https://github.com/effil.png","language":"C++","readme":"# Effil\n[![Generic badge](https://img.shields.io/badge/Lua-5.1-44cc11.svg)](https://shields.io/)\n[![Generic badge](https://img.shields.io/badge/Lua-5.2-44cc11.svg)](https://shields.io/)\n[![Generic badge](https://img.shields.io/badge/Lua-5.3-44cc11.svg)](https://shields.io/)\n[![Generic badge](https://img.shields.io/badge/LuaJIT-2.0-44cc11.svg)](https://shields.io/)\n\n[![LuaRocks](https://img.shields.io/luarocks/v/mihacooper/effil.svg)](https://luarocks.org/modules/mihacooper/effil)\n[![license](https://img.shields.io/github/license/mashape/apistatus.svg)]()\n[![Join the chat at https://gitter.im/effil-chat/Lobby](https://badges.gitter.im/effil-chat/Lobby.svg)](https://gitter.im/effil-chat/Lobby?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n| Linux + MacOS | Windows |\n| ------------- | ------- |\n| [![build \u0026 run tests](https://github.com/effil/effil/actions/workflows/nix_ci.yml/badge.svg)](https://github.com/effil/effil/actions/workflows/nix_ci.yml) | [![Build status](https://github.com/effil/effil/actions/workflows/win_ci.yml/badge.svg)](https://github.com/effil/effil/actions/workflows/win_ci.yml) |\n\nEffil is a multithreading library for Lua.\nIt allows to spawn native threads and safe data exchange.\nEffil has been designed to provide clear and simple API for lua developers.\n\nEffil supports lua 5.1, 5.2, 5.3 and LuaJIT.\nRequires C++14 compiler compliance. Tested with GCC 4.9+, clang 3.8 and Visual Studio 2015.\n\n# Table Of Contents\n* [How to install](#how-to-install)\n* [Quick guide](#quick-guide)\n* [Important notes](#important-notes)\n* [Blocking and nonblocking operations](#blocking-and-nonblocking-operations)\n* [Function's upvalues](#functions-upvalues)\n* [Thread cancellation and pausing](#thread-cancellation-and-pausing)\n* [API Reference](#api-reference)\n   * [Thread](#thread)\n      * [effil.thread()](#runner--effilthreadfunc)\n    * [Thread runner](#thread-runner)\n      * [runner()](#thread--runner)\n      * [runner.path](#runnerpath)\n      * [runner.cpath](#runnercpath)\n      * [runner.step](#runnerstep)\n    * [Thread handle](#thread-handle)\n      * [thread:status()](#status-err-stacktrace--threadstatus)\n      * [thread:get()](#--threadgettime-metric)\n      * [thread:wait()](#threadwaittime-metric)\n      * [thread:cancel()](#threadcanceltime-metric)\n      * [thread:pause()](#threadpausetime-metric)\n      * [thread:resume()](#threadresume)\n    * [Thread helpers](#thread-helpers)\n      * [effil.thread_id()](#id--effilthread_id)\n      * [effil.yield()](#effilyield)\n      * [effil.sleep()](#effilsleeptime-metric)\n      * [effil.hardware_threads()](#effilhardware_threads)\n      * [effil.pcall()](#status---effilpcallfunc)\n    * [Table](#table)\n      * [effil.table()](#table--effiltabletbl)\n      * [__newindex: table[key] = value](#tablekey--value)\n      * [__index: value = table[key]](#value--tablekey)\n      * [effil.setmetatable()](#tbl--effilsetmetatabletbl-mtbl)\n      * [effil.getmetatable()](#mtbl--effilgetmetatabletbl)\n      * [effil.rawset()](#tbl--effilrawsettbl-key-value)\n      * [effil.rawget()](#value--effilrawgettbl-key)\n      * [effil.G](#effilg)\n      * [effil.dump()](#result--effildumpobj)\n    * [Channel](#channel)\n      * [effil.channel()](#channel--effilchannelcapacity)\n      * [channel:push()](#pushed--channelpush)\n      * [channel:pop()](#--channelpoptime-metric)\n      * [channel:size()](#size--channelsize)\n    * [Garbage collector](#garbage-collector)\n      * [effil.gc.collect()](#effilgccollect)\n      * [effil.gc.count()](#count--effilgccount)\n      * [effil.gc.step()](#old_value--effilgcstepnew_value)\n      * [effil.gc.pause()](#effilgcpause)\n      * [effil.gc.resume()](#effilgcresume)\n      * [effil.gc.enabled()](#enabled--effilgcenabled)\n    * [Other methods](#othermethods)\n      * [effil.size()](#size--effilsizeobj)\n      * [effil.type()](#effiltype)\n\n\n# How to install\n### Build from src on Linux and Mac\n1. `git clone --recursive https://github.com/effil/effil effil`\n2. `cd effil \u0026\u0026 mkdir build \u0026\u0026 cd build`\n3. `cmake .. \u0026\u0026 make install`\n4. Copy effil.so to your project.\n\n### From lua rocks\n`luarocks install effil`\n\n# Quick guide\nAs 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.\n\nEffil library provides three major abstractions:\n1. `effil.thread` - provides API for threads management.\n2. `effil.table` - provides API for tables management. Tables can be shared between threads.\n3. `effil.channel` - provides First-In-First-Out container for sequential data exchange.\n\nAnd bunch of utilities to handle threads and tables as well.\n\n### Examples\n\u003cdetails\u003e\n   \u003csummary\u003e\u003cb\u003eSpawn the thread\u003c/b\u003e\u003c/summary\u003e\n   \u003cp\u003e\n\n```lua\nlocal effil = require(\"effil\")\n\nfunction bark(name)\n    print(name .. \" barks from another thread!\")\nend\n\n-- run funtion bark in separate thread with name \"Spaky\"\nlocal thr = effil.thread(bark)(\"Sparky\")\n\n-- wait for completion\nthr:wait()\n```\n\n**Output:**\n`Sparky barks from another thread!`\n   \u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n   \u003csummary\u003e\u003cb\u003eSharing data with effil.channel\u003c/b\u003e\u003c/summary\u003e\n   \u003cp\u003e\n\n```lua\nlocal effil = require(\"effil\")\n\n-- channel allow to push data in one thread and pop in other\nlocal channel = effil.channel()\n\n-- writes some numbers to channel\nlocal function producer(channel)\n    for i = 1, 5 do\n        print(\"push \" .. i)\n        channel:push(i)\n    end\n    channel:push(nil)\nend\n\n-- read numbers from channels\nlocal function consumer(channel)\n    local i = channel:pop()\n    while i do\n        print(\"pop \" .. i)\n        i = channel:pop()\n    end\nend\n\n-- run producer\nlocal thr = effil.thread(producer)(channel)\n\n-- run consumer\nconsumer(channel)\n\nthr:wait()\n```\n**Output:**\n```\npush 1\npush 2\npop 1\npop 2\npush 3\npush 4\npush 5\npop 3 \npop 4\npop 5\n```\n   \u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n   \u003csummary\u003e\u003cb\u003eSharing data with effil.table\u003c/b\u003e\u003c/summary\u003e\n   \u003cp\u003e\n\n```lua\neffil = require(\"effil\")\n\n-- effil.table transfers data between threads\n-- and behaves like regualr lua table\nlocal storage = effil.table { string_field = \"first value\" }\nstorage.numeric_field = 100500\nstorage.function_field = function(a, b) return a + b end\nstorage.table_field = { fist = 1, second = 2 }\n\nfunction check_shared_table(storage)\n   print(storage.string_field)\n   print(storage.numeric_field)\n   print(storage.table_field.first)\n   print(storage.table_field.second)\n   return storage.function_field(1, 2)\nend\n\nlocal thr = effil.thread(check_shared_table)(storage)\nlocal ret = thr:get()\nprint(\"Thread result: \" .. ret)\n\n```\n**Output:**\n```\nfirst value\n100500\n1\n2\nThread result: 3\n```\n   \u003c/p\u003e\n\u003c/details\u003e\n\n# Important notes\nEffil allows to transmit data between threads (Lua interpreter states) using `effil.channel`, `effil.table` or directly as parameters of `effil.thread`.\n - Primitive types are transmitted 'as is' by copy: `nil`, `boolean`, `number`, `string`\n - Functions are dumped using [`lua_dump`](#https://www.lua.org/manual/5.3/manual.html#lua_dump). Upvalues are captured according to [the rules](#functions-upvalues).\n    - 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).  \n    **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.\n - **Userdata and Lua threads (coroutines)** are not supported.\n - 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:\n```Lua\n-- Example #1\nt = {}\nfor i = 1, 100 do\n   t[i] = i\nend\nshared_table = effil.table(t)\n\n-- Example #2\nt = effil.table()\nfor i = 1, 100 do\n   t[i] = i\nend\n```\nIn 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.\n\n## Blocking and nonblocking operations:\nAll operations which use time metrics can be blocking or non blocking and use following API:\n`(time, metric)` where `metric` is time interval like `'s'` (seconds) and `time` is a number of intervals.\n\nExample:\n- `thread:get()` - infinitely wait for thread completion.\n- `thread:get(0)` - non blocking get, just check is thread finished and return\n- `thread:get(50, \"ms\")` - blocking wait for 50 milliseconds.\n\nList of available time intervals:\n- `ms` - milliseconds;\n- `s` - seconds (default);\n- `m` - minutes;\n- `h` - hours.\n\nAll blocking operations (even in non blocking mode) are interruption points. Thread hanged in such operation can be interrupted by invoking [thread:cancel()](#threadcanceltime-metric) method.\n\u003cdetails\u003e\n   \u003csummary\u003e\u003cb\u003eExample\u003c/b\u003e\u003c/summary\u003e\n   \u003cp\u003e\n\n```lua\nlocal effil = require \"effil\"\n\nlocal worker = effil.thread(function()\n    effil.sleep(999) -- worker will hang for 999 seconds\nend)()\n\nworker:cancel(1) -- returns true, cause blocking operation was interrupted and thread was cancelled\n```\n   \u003c/p\u003e\n\u003c/details\u003e\n\n\n## Function's upvalues\nWorking with functions Effil serializes and deserializes them using [`lua_dump`](#https://www.lua.org/manual/5.3/manual.html#lua_dump) and [`lua_load`](#https://www.lua.org/manual/5.3/manual.html#lua_load) methods. All function's upvalues are stored following the same [rules](#important-notes) as usual. If function has **upvalue of unsupported type** this function cannot be transmitted to Effil. You will get error in that case.\n\nWorking 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:\n * *Lua = 5.1*: function environment is not stored at all (due to limitations of lua_setfenv we cannot use userdata)\n * *Lua \u003e 5.1*: Effil serialize and store function environment only if it's not equal to global environment (`_ENV ~= _G`).\n\n## Thread cancellation and pausing\nThe [`effil.thread`](#runner--effilthreadfunc) can be paused and cancelled using corresponding methods of thread object [`thread:cancel()`](#threadcanceltime-metric) and [`thread:pause()`](#threadpausetime-metric).  \nThread that you try to interrupt can be interrupted in two execution points: explicit and implicit.\n  - Explicit points are [`effil.yield()`](#effilyield)\n      \u003cdetails\u003e\n      \u003csummary\u003eExample of explicit interruption point\u003c/summary\u003e\n      \u003cp\u003e\n\n      ```lua\n      local thread = effil.thread(function()\n         while true do\n            effil.yield()\n         end\n         -- will never reach this line\n      end)()\n      thread:cancel()\n      ```\n\n      \u003c/p\u003e\n      \u003c/details\u003e\n  - Implicit points are lua debug hook invocation which is set using [lua_sethook](https://www.lua.org/manual/5.3/manual.html#lua_sethook) with LUA_MASKCOUNT.  \n    Implicit points are optional and enabled only if [thread_runner.step](#runnerstep) \u003e 0.\n      \u003cdetails\u003e\n      \u003csummary\u003eExample of implicit interruption point\u003c/summary\u003e\n      \u003cp\u003e\n\n      ```lua\n      local thread_runner = effil.thread(function()\n         while true do\n         end\n         -- will never reach this line\n      end)\n      thread_runner.step = 10\n      thread = thread_runner()\n      thread:cancel()\n      ```\n\n      \u003c/p\u003e\n      \u003c/details\u003e\n  - Additionally thread can be cancelled (but not paused) in any [blocking or non-blocking waiting operation](#blocking-and-nonblocking-operations).  \n      \u003cdetails\u003e\n      \u003csummary\u003eExample\u003c/summary\u003e\n      \u003cp\u003e\n\n      ```lua\n      local channel = effil.channel()\n      local thread = effil.thread(function()\n         channel:pop() -- thread hangs waiting infinitely\n         -- will never reach this line\n      end)()\n      thread:cancel()\n      ```\n\n      \u003c/p\u003e\n      \u003c/details\u003e\n\n   **How does cancellation works?**  \n   When you cancel thread it generates lua [`error`](https://lua.org.ru/manual_ru.html#pdf-error) with message `\"Effil: thread is cancelled\"` when it reaches any interruption point. It means that you can catch this error using [`pcall`](https://lua.org.ru/manual_ru.html#pdf-pcall) but thread will generate new error on next interruption point.  \n   If you want to catch your own error but pass cancellation error you can use [effil.pcall()](#status---effilpcallfunc).  \n   Status of cancelled thread will be equal to `cancelled` only if it finished with cancellation error. It means that if you catch cancellation error thread may finished with `completed` status or `failed` status if there will be some another error.\n\n# API Reference\n\n## Thread\n`effil.thread` is the way to create a thread. Threads can be stopped, paused, resumed and cancelled.\nAll operation with threads can be synchronous (with optional timeout) or asynchronous.\nEach thread runs with its own lua state.\n\nUse `effil.table` and `effil.channel` to transmit data over threads.\nSee example of thread usage [here](#examples).\n\n### `runner = effil.thread(func)`\nCreates thread runner. Runner spawns new thread for each invocation. \n\n**input**: *func* - Lua function\n\n**output**: *runner* - [thread runner](#thread-runner) object to configure and run a new thread\n\n## Thread runner\nAllows to configure and run a new thread.\n### `thread = runner(...)`\nRun captured function with specified arguments in separate thread and returns [thread handle](#thread-handle).\n\n**input**: Any number of arguments required by captured function.\n\n**output**: [Thread handle](#thread-handle) object.\n\n### `runner.path`\nIs a Lua `package.path` value for new state. Default value inherits `package.path` form parent state.\n### `runner.cpath`\nIs a Lua `package.cpath` value for new state. Default value inherits `package.cpath` form parent state.\n### `runner.step`\nNumber 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](#effilyield).\n\n## Thread handle\nThread handle provides API for interaction with thread.\n\n### `status, err, stacktrace = thread:status()`\nReturns thread status.\n\n**output**:\n- `status` - string values describes status of thread. Possible values are: `\"running\", \"paused\", \"cancelled\", \"completed\" and \"failed\"`.\n- `err` - error message, if any. This value is specified only if thread status == `\"failed\"`.\n- `stacktrace` - stacktrace of failed thread. This value is specified only if thread status == `\"failed\"`.\n\n### `... = thread:get(time, metric)`\nWaits for thread completion and returns function result or nothing in case of error.\n\n**input**: Operation timeout in terms of [time metrics](#blocking-and-nonblocking-operations)\n\n**output**: Results of captured function invocation or nothing in case of error.\n\n### `thread:wait(time, metric)`\nWaits for thread completion and returns thread status.\n\n**input**: Operation timeout in terms of [time metrics](#blocking-and-nonblocking-operations)\n\n**output**: Returns status of thread. The output is the same as [`thread:status()`](#status-err--threadstatus)\n\n### `thread:cancel(time, metric)`\nInterrupts 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`.\n\n**input**: Operation timeout in terms of [time metrics](#blocking-and-nonblocking-operations)\n\n**output**: Returns `true` if thread was stopped or `false`.\n\n### `thread:pause(time, metric)`\nPauses 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.\n\n**input**: Operation timeout in terms of [time metrics](#blocking-and-nonblocking-operations)\n\n**output**: Returns `true` if thread was paused or `false`. If the thread is completed function will return `false`\n\n### `thread:resume()`\nResumes paused thread. Function resumes thread immediately if it was paused. This function does nothing for completed thread. Function has no input and output parameters.\n\n## Thread helpers\n### `id = effil.thread_id()`\nGives unique identifier.\n\n**output**:  returns unique string `id` for *current* thread.\n\n### `effil.yield()`\nExplicit cancellation point. Function checks *cancellation* or *pausing* flags of current thread and if it's required it performs corresponding actions (cancel or pause thread).\n\n### `effil.sleep(time, metric)`\nSuspend current thread.\n\n**input**: [time metrics](#blocking-and-nonblocking-operations) arguments.\n\n### `effil.hardware_threads()`\nReturns the number of concurrent threads supported by implementation.\nBasically forwards value from [std::thread::hardware_concurrency](https://en.cppreference.com/w/cpp/thread/thread/hardware_concurrency).  \n**output**: number of concurrent hardware threads.\n\n### `status, ... = effil.pcall(func, ...)`\nWorks exactly the same way as standard [pcall](https://www.lua.org/manual/5.3/manual.html#pdf-pcall) except that it will not catch thread cancellation error caused by [thread:cancel()](#threadcanceltime-metric) call.  \n\n**input:**\n  - func - function to call\n  - ... - arguments to pass to functions  \n\n**output:**\n  - status - `true` if no error occurred, `false` otherwise\n  - ... - in case of error return one additional result with message of error, otherwise return function call results\n\n## Table\n`effil.table` is a way to exchange data between effil threads. It behaves almost like standard lua tables.\nAll operations with shared table are thread safe. **Shared table stores** primitive types (number, boolean, string), function, table, light userdata and effil based userdata. **Shared table doesn't store** lua threads (coroutines) or arbitrary userdata. See examples of shared table usage [here](#examples)\n\n### Notes: shared tables usage\n\nUse **Shared tables with regular tables**. If you want to store regular table in shared table, effil will implicitly dump origin table into new shared table. **Shared tables always stores subtables as shared tables.**\n\nUse **Shared tables with functions**. If you store function in shared table, effil implicitly dumps this function and saves it as string (and it's upvalues). All function's upvalues will be captured according to [following rules ](#functions-upvalues).\n\n### `table = effil.table(tbl)`\nCreates new **empty** shared table.\n\n**input**: `tbl` - is *optional* parameter, it can be only regular Lua table which entries will be **copied** to shared table.\n\n**output**: new instance of empty shared table. It can be empty or not, depending on `tbl` content.\n\n### `table[key] = value` \nSet a new key of table with specified value.\n\n**input**:\n- `key` - any value of supported type. See the list of [supported types](#important-notes)\n- `value` - any value of supported type. See the list of [supported types](#important-notes)\n\n### `value = table[key]` \nGet a value from table with specified key.\n\n**input**: `key` - any value of supported type. See the list of [supported types](#important-notes)\n\n**output**: `value` - any value of supported type. See the list of [supported types](#important-notes)\n\n### `tbl = effil.setmetatable(tbl, mtbl)`\nSets a new metatable to shared table. Similar to standard [setmetatable](https://www.lua.org/manual/5.3/manual.html#pdf-setmetatable).\n\n**input**:\n- `tbl` should be shared table for which you want to set metatable. \n- `mtbl` should be regular table or shared table which will become a metatable. If it's a regular table *effil* will create a new shared table and copy all fields of `mtbl`. Set `mtbl` equal to `nil` to delete metatable from shared table.\n\n**output**: just returns `tbl` with a new *metatable* value similar to standard Lua *setmetatable* method.\n\n### `mtbl = effil.getmetatable(tbl)`\nReturns current metatable. Similar to standard [getmetatable](https://www.lua.org/manual/5.3/manual.html#pdf-getmetatable)\n\n**input**: `tbl` should be shared table. \n\n**output**: returns *metatable* of specified shared table. Returned table always has type `effil.table`. Default metatable is `nil`.\n\n### `tbl = effil.rawset(tbl, key, value)`\nSet table entry without invoking metamethod `__newindex`. Similar to standard [rawset](https://www.lua.org/manual/5.3/manual.html#pdf-rawset)\n\n**input**:\n- `tbl` is shared table. \n- `key` - key of table to override. The key can be of any [supported type](#important-notes).\n- `value` - value to set. The value can be of any [supported type](#important-notes).\n\n**output**: returns the same shared table `tbl`\n\n### `value = effil.rawget(tbl, key)`\nGets table value without invoking metamethod `__index`. Similar to standard [rawget](https://www.lua.org/manual/5.3/manual.html#pdf-rawget)\n\n**input**:\n- `tbl` is shared table. \n- `key` - key of table to receive a specific value. The key can be of any [supported type](#important-notes).\n\n**output**: returns required `value` stored under a specified `key`\n\n### `effil.G`\nIs a global predefined shared table. This table always present in any thread (any Lua state).\n```lua\neffil = require \"effil\"\n\nfunction job()\n   effil = require \"effil\"\n   effil.G.key = \"value\"\nend\n\neffil.thread(job)():wait()\nprint(effil.G.key) -- will print \"value\"\n```\n\n### `result = effil.dump(obj)`\nTruns `effil.table` into regular Lua table.\n```lua\ntbl = effil.table({})\neffil.type(tbl) -- 'effil.table'\neffil.type(effil.dump(tbl))  -- 'table'\n```\n\n## Channel\n`effil.channel` is a way to sequentially exchange data between effil threads. It allows to push message from one thread and pop  it from another. Channel's **message** is a set of values of [supported types](#important-notes). All operations with channels are thread safe. See examples of channel usage [here](#examples)\n\n### `channel = effil.channel(capacity)`\nCreates a new channel.\n\n**input**: optional *capacity* of channel. If `capacity` equals to `0` or to `nil` size of channel is unlimited. Default capacity is `0`.\n\n**output**: returns a new instance of channel.\n\n### `pushed = channel:push(...)`\nPushes message to channel.\n\n**input**: any number of values of [supported types](#important-notes). Multiple values are considered as a single channel message so one *push* to channel decreases capacity by one.\n\n**output**: `pushed` is equal to `true` if value(-s) fits channel capacity, `false` otherwise.\n\n### `... = channel:pop(time, metric)`\nPop message from channel. Removes value(-s) from channel and returns them. If the channel is empty wait for any value appearance.\n\n**input**: waiting timeout in terms of [time metrics](#blocking-and-nonblocking-operations) (used only if channel is empty).\n\n**output**: variable amount of values which were pushed by a single [channel:push()](#pushed--channelpush) call.\n\n### `size = channel:size()`\nGet actual amount of messages in channel.\n\n**output**: amount of messages in channel.\n\n## Garbage collector\nEffil provides custom garbage collector for `effil.table` and `effil.channel` (and functions with captured upvalues). It allows safe manage cyclic references for tables and channels in multiple threads. However it may cause extra memory usage. `effil.gc` provides a set of method configure effil garbage collector. But, usually you don't need to configure it.\n\n### Garbage collection trigger\nGarbage collector perform it's work when effil creates new shared object (table, channel and functions with captured upvalues).\nEach iteration GC checks amount of objects. If amount of allocated objects becomes higher then specific threshold value GC starts garbage collecting. Threshold value is calculated as `previous_count * step`, where `previous_count` - amount of objects on previous iteration (**100** by default) and `step` is a numerical coefficient specified by user (**2.0** by default).\n\nFor example: if GC `step` is `2.0` and amount of allocated objects is `120` (left after previous GC iteration) then GC will start to collect garbage when amount of allocated objects will be equal to`240`.\n\n### How to cleanup all dereferenced objects \nEach thread represented as separate Lua state with own garbage collector.\nThus, objects will be deleted eventually.\nEffil objects itself also managed by GC and uses `__gc` userdata metamethod as deserializer hook.\nTo force objects deletion:\n1. invoke standard `collectgarbage()` in all threads.\n2. invoke `effil.gc.collect()` in any thread.\n\n### `effil.gc.collect()`\nForce garbage collection, however it doesn't guarantee deletion of all effil objects.\n\n### `count = effil.gc.count()`\nShow number of allocated shared tables and channels.\n\n**output**: returns current number of allocated objects. Minimum value is 1, `effil.G` is always present. \n\n### `old_value = effil.gc.step(new_value)`\nGet/set GC memory step multiplier. Default is `2.0`. GC triggers collecting when amount of allocated objects growth in `step` times.\n\n**input**: `new_value` is optional value of step to set. If it's `nil` then function will just return a current value.\n\n**output**: `old_value` is current (if `new_value == nil`) or previous (if `new_value ~= nil`) value of step.\n\n### `effil.gc.pause()`\nPause GC. Garbage collecting will not be performed automatically. Function does not have any *input* or *output*\n\n### `effil.gc.resume()`\nResume GC. Enable automatic garbage collecting.\n\n### `enabled = effil.gc.enabled()`\nGet GC state.\n\n**output**: return `true` if automatic garbage collecting is enabled or `false` otherwise. By default returns `true`.\n\n## Other methods\n\n### `size = effil.size(obj)`\nReturns number of entries in Effil object.\n\n**input**: `obj` is [shared table](#table) or [channel](#channel).\n\n**output**: number of entries in [shared table](#table) or number of messages in [channel](#channel)\n\n### `type = effil.type(obj)`\nThreads, channels and tables are userdata. Thus, `type()` will return `userdata` for any type. If you want to detect type more precisely use `effil.type`. It behaves like regular `type()`, but it can detect effil specific userdata.\n\n**input**: `obj` is object of any type.\n\n**output**: string name of type. If `obj` is Effil object then function returns string like `effil.table` in other cases it returns result of [lua_typename](https://www.lua.org/manual/5.3/manual.html#lua_typename) function.\n\n```Lua\neffil.type(effil.thread()) == \"effil.thread\"\neffil.type(effil.table()) == \"effil.table\"\neffil.type(effil.channel()) == \"effil.channel\"\neffil.type({}) == \"table\"\neffil.type(1) == \"number\"\n```\n\n","funding_links":[],"categories":["C++","Resources"],"sub_categories":["Multitasking"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffil%2Feffil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feffil%2Feffil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffil%2Feffil/lists"}