{"id":13759474,"url":"https://github.com/osch/lua-mtint","last_synced_at":"2025-04-23T01:24:10.420Z","repository":{"id":66985040,"uuid":"147553680","full_name":"osch/lua-mtint","owner":"osch","description":"Make threads and coroutines interruptible for the Lua scripting language, see https://github.com/osch/lua-mtint#mtint","archived":false,"fork":false,"pushed_at":"2023-11-10T00:00:31.000Z","size":57,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T20:51:09.403Z","etag":null,"topics":["lua","lua-binding","lua-library","multithreading"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-05T17:16:44.000Z","updated_at":"2023-09-18T06:28:08.000Z","dependencies_parsed_at":"2024-08-03T13:13:25.001Z","dependency_job_id":null,"html_url":"https://github.com/osch/lua-mtint","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osch%2Flua-mtint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osch%2Flua-mtint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osch%2Flua-mtint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osch%2Flua-mtint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osch","download_url":"https://codeload.github.com/osch/lua-mtint/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250350112,"owners_count":21416070,"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":["lua","lua-binding","lua-library","multithreading"],"created_at":"2024-08-03T13:00:53.685Z","updated_at":"2025-04-23T01:24:10.398Z","avatar_url":"https://github.com/osch.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"# mtint \n[![Licence](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE)\n[![build status](https://github.com/osch/lua-mtint/workflows/test/badge.svg)](https://github.com/osch/lua-mtint/actions/workflows/test.yml)\n[![Build status](https://ci.appveyor.com/api/projects/status/g5sijdrvdx6vviqr/branch/master?svg=true)](https://ci.appveyor.com/project/osch/lua-mtint/branch/master)\n[![Install](https://img.shields.io/badge/Install-LuaRocks-brightgreen.svg)](https://luarocks.org/modules/osch/mtint)\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n\nMake threads and coroutines interruptible for the [Lua] scripting language.\n\nThis package provides a way to make arbitrary Lua interpreter states (i.e. main\nstates and couroutines) interruptible from concurrently running threads. \nThe implementation is independent from the underlying threading \nlibrary (e.g. [Lanes] or [lua-llthreads2]).\n\nThe general principle is to interrupt a Lua state by installing a debug hook \nthat triggers an error. This can be useful for applications with interactive user \ninterface when a user wants to abort a long running background task or a user \nsupplied script that is stuck in an infinite loop.\n\nThis package is also available via LuaRocks, see https://luarocks.org/modules/osch/mtint.\n\n[Lua]:               https://www.lua.org\n[Lanes]:             https://luarocks.org/modules/benoitgermain/lanes\n[lua-llthreads2]:    https://luarocks.org/modules/moteus/lua-llthreads2\n\nSee below for full [reference documentation](#documentation).\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n\n#### Requirements\n\n   * Tested operating systems: Linux, Windows, MacOS\n   * Other Unix variants: could work, but untested, required are:\n      * gcc atomic builtins or C11 `stdatomic.h`\n      * `pthread.h` or C11 `threads.h`\n   * Tested Lua versions: 5.1, 5.2, 5.3, 5.4, luajit 2.0 \u0026 2.1\n       * Lua 5.2 - 5.4: full support, main states and coroutines are interruptible.\n       * Lua 5.1: coroutines cannot be interrupted, only main states.\n       * LuaJIT: same restrictions than Lua 5.1, JIT compiled pure machine code \n                 not considering the debug hook cannot be interrupted.\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n\n## Examples\n\nFor the examples [llthreads2](https://luarocks.org/modules/moteus/lua-llthreads2)\nis used as low level multi-threading implementation and \n[mtmsg](https://luarocks.org/modules/osch/mtmsg) is used for inter-thread\nmessage passing.\n\n#### Example 1\n\nA parallel running thread passes its interruptible id as integer to \nthe main thread and enters an infinite loop. The main thread takes the id and\ninterrupts the infinite loop:\n\n```lua\nlocal llthreads = require(\"llthreads2.ex\")\nlocal mtmsg     = require(\"mtmsg\")\nlocal mtint     = require(\"mtint\")\nlocal threadOut = mtmsg.newbuffer()\nlocal thread    = llthreads.new(function(outId)\n                                    local loadstring = loadstring or load\n                                    local mtmsg      = require(\"mtmsg\")\n                                    local mtint      = require(\"mtint\")\n                                    local threadOut  = mtmsg.buffer(outId)\n                                    threadOut:addmsg(mtint.id())\n                                    local x = 1\n                                    while true do \n                                        x = x + 1 \n                                        -- do some work, prevent jit compilation for LuaJIT:\n                                        assert(x == loadstring(\"return \"..x)())\n                                    end\n                                end,\n                                threadOut:id())\nthread:start()\nlocal interruptibleId = threadOut:nextmsg()\nmtint.interrupt(interruptibleId)\nlocal _, err = thread:join()\nassert(err:match(mtint.error.interrupted))\n```\n\nThe `loadstring(...)` is necessary for LuaJIT because otherwise the infinite loop would \nbe compiled by the JIT to pure machine code that does not consider the lua debug \nhook which is needed for interrupting.\n\n#### Example 2\n\nIn the second example a coroutine is interrupted while it runs concurrently in \nanother thread. This example does only work with Lua 5.2 \u0026 5.3 (interrupting \ncoroutines is not supported for Lua 5.1).\n\n```lua\nlocal llthreads = require(\"llthreads2.ex\")\nlocal mtmsg     = require(\"mtmsg\")\nlocal mtint     = require(\"mtint\")\nlocal threadOut = mtmsg.newbuffer()\nlocal thread    = llthreads.new(function(outId)\n                                    local mtmsg     = require(\"mtmsg\")\n                                    local mtint     = require(\"mtint\")\n                                    local threadOut = mtmsg.buffer(outId)\n                                    local c = coroutine.create(function()\n                                        coroutine.yield(mtint.id())\n                                        threadOut:addmsg(mtint.id())\n                                        local x = 1\n                                        while true do x = x + 1 end\n                                    end)\n                                    local ok, cid = coroutine.resume(c)\n                                    assert(ok and cid == mtint.id(c))\n                                    local ok, err = coroutine.resume(c)\n                                    assert(not ok and err:match(mtint.error.interrupted))\n                                end,\n                                threadOut:id())\nthread:start()\nlocal interruptibleId = threadOut:nextmsg()\nmtint.interrupt(interruptibleId)\nassert(thread:join())\n```\n\n#### Example 3\n\nThe interrupting main thread communicates via [mtmsg](https://luarocks.org/modules/osch/mtmsg)\nwith an interrupt handler on the interrupted thread:\n\n```lua\nlocal llthreads = require(\"llthreads2.ex\")\nlocal mtmsg     = require(\"mtmsg\")\nlocal mtint     = require(\"mtint\")\n\nlocal threadIn  = mtmsg.newbuffer()\nlocal threadOut = mtmsg.newbuffer()\n\nlocal thread = llthreads.new(function(threadInId, threadOutId)\n                                local loadstring = loadstring or load\n                                local mtmsg      = require(\"mtmsg\")\n                                local mtint      = require(\"mtint\")\n                                local threadIn   = mtmsg.buffer(threadInId)\n                                local threadOut  = mtmsg.buffer(threadOutId)\n                                local stop       = false\n                                local counter    = 0\n                                mtint.sethook(function()\n                                    print(\"counter\", counter)\n                                    local cmd = threadIn:nextmsg()\n                                    if cmd == \"CONTINUE\" then\n                                        threadOut:addmsg(\"OK\")\n                                    end\n                                    if cmd == \"QUIT\" then\n                                        threadOut:addmsg(\"QUITTING\")\n                                        stop = true\n                                    end\n                                end)\n                                threadOut:addmsg(mtint.id())\n                                while not stop do \n                                    counter = counter + 1\n                                    -- do some work, prevent jit compilation for LuaJIT:\n                                    assert(counter == loadstring(\"return \"..counter)())\n                                end\n                            end,\n                            threadIn:id(), threadOut:id())\nthread:start()\nlocal intId = threadOut:nextmsg()\n\nthreadIn:addmsg(\"CONTINUE\")\nmtint.interrupt(intId)\nassert(\"OK\" == threadOut:nextmsg())\n\nthreadIn:addmsg(\"CONTINUE\")\nmtint.interrupt(intId)\nassert(\"OK\" == threadOut:nextmsg())\n\nthreadIn:addmsg(\"QUIT\")\nmtint.interrupt(intId)\nassert(\"QUITTING\" == threadOut:nextmsg())\n\nthread:join()\n```\n\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n\n## Documentation\n\n   * [Module Functions](#module-functions)\n       * mtint.id()\n       * mtint.sethook()\n       * mtint.interrupt()\n   * [Errors](#errors)\n       * mtint.error.interrupt\n       * mtint.error.not_supported\n       * mtint.error.unknown_object\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n\n### Module Functions\n\n* **`mtint.id([co])`**\n\n  Returns the interruptible id.\n  \n    * *co*  - optional coroutine whose interruptible id is returned. If not\n              given, the interruptible id of the current running coroutine or\n              main state is returned.\n\n  Possible errors: *mtint.error.not_supported*\n\n\n\n* **`mtint.sethook([co,]func)`**\n\n  Sets an interrupt handler function. The interrupt handler is called on the interrupted thread \n  if *mtint.interrupt()* is called from somewhere else. If the interrupt handler is set \n  to `nil`, an error *mtint.error.interrupt* is raised on the interrupted thread.\n  \n  \n    * *co*   - optional coroutine whose interrupt handler is to be set. If not\n               given, the interrupt handler of the current running coroutine or\n               main state is set.\n\n    * *func* - interrupt handler function or `nil`.\n    \n  Possible errors: *mtint.error.not_supported*\n\n\n* **`mtint.interrupt(id[,flag])`**\n\n  * *id*   - integer, the interruptible id of a main state or coroutine that can\n             be obtained by *mtint.id()*.\n\n  * *flag* - optional boolean, if not specified or *nil* the state\n             is only interrupted once, if *true* the state is interrupted\n             at every operation again, if *false* the state is no\n             longer interrupted.\n\n  Possible errors: *mtint.error.unknown_object*\n\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n\n### Errors\n\n* All errors raised by this module are string values. For evaluation purposes \n  special error strings are available in the table `mtint.error`, example:\n\n  ```lua\n  local mtint = require(\"mtint\")\n  local _, err = pcall(function() \n      mtint.interrupt(1)\n  end)\n  assert(err:match(mtint.error.unknown_object))\n  assert(mtint.error.unknown_object == \"mtint.error.unknown_object\")\n  ```\n\n* **`mtint.error.interrupt`**\n\n  The current state or coroutine has been interrupted by invoking *mtint.interrupt()*\n  and no interrupt handler function was set via *mtint.sethook()*.\n\n* **`mtint.error.not_supported`**\n\n  Under Lua 5.1 it is not supported to obtain an interruptible id from a couroutine,\n  only Lua main states are allowed.\n\n* **`mtint.error.unknown_object`**\n\n  An interruptible id has been given to *mtint.interrupt()* and the corresponding\n  interruptible object cannot be found. One reason could be, that the object\n  has been garbage collected, example:\n\n  ```lua\n  local mtint = require(\"mtint\")\n  local c = coroutine.create(function() end)\n  local id = mtint.id(c)\n  mtint.interrupt(id)\n  c = nil\n  collectgarbage()\n  local _, err = pcall(function()\n      mtint.interrupt(id)\n  end)\n  assert(err:match(mtint.error.unknown_object))\n  ```\n\nEnd of document.\n\n\u003c!-- ---------------------------------------------------------------------------------------- --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosch%2Flua-mtint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosch%2Flua-mtint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosch%2Flua-mtint/lists"}