# openresty/luajit2: OpenResty Extensions and C APIs

> openresty/luajit2 combines LuaJIT 2 with APIs for table inspection, per-thread pointer storage, JIT PRNG state, and thread recycling. It is aimed at OpenResty core development and embedders that need the branch-specific interface.

**openresty/luajit2** — OpenResty's Branch of LuaJIT 2

- Repository: https://github.com/openresty/luajit2
- Website: https://luajit.org/luajit.html
- Stars: 1,461 · Forks: 252
- Language: C
- License: NOASSERTION
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/openresty-luajit2

## Why openresty/luajit2 Differs from Mainline LuaJIT

openresty/luajit2 addresses the need for a LuaJIT 2 branch that carries changes used by OpenResty while continuing to synchronize with the upstream `LuaJIT/LuaJIT` project. The README calls it the official OpenResty branch and explicitly says it should not be considered a fork in the ordinary sense because upstream changes are regularly synchronized.

That distinction defines the project. Its Lua and C APIs include additions that have not been merged upstream or are not expected to be merged. The README also lists OpenResty-specific macros, updated JIT defaults, string hashing changes, bytecode options, and miscellaneous changes. Consequently, openresty/luajit2 is most relevant to OpenResty core developers and C-based embedders. It can also serve Lua programmers who need the added table functions, but those programmers need to accept the branch compatibility boundary.

Mainline LuaJIT is the more direct alternative when a runtime must follow the primary upstream project. openresty/luajit2 is appropriate when code depends on its additional interfaces or when the deployment environment already standardizes on the OpenResty branch. The repository uses `v2.1-agentzh` as its default branch.

This project does not solve every LuaJIT packaging or deployment concern by itself. Its Makefile is designed around building the runtime, while application packaging, operating system support, and third-party module compatibility remain separate concerns. The README and top-level build files do not describe such a package layer.

## OpenResty Table Inspection and Shallow Copying

The branch adds four Lua modules for common table operations. `table.isempty` returns `true` when a table has no non-nil array elements or key-value pairs. `table.isarray` checks whether a table is purely array-like, with an empty table treated as an array. `table.nkeys` counts entries from both the array and hash portions, while `table.clone` returns a shallow copy.

The README states that all four APIs can be JIT compiled. It does not publish benchmark results for these functions, so the branch should not be presented as faster merely because they are eligible for JIT compilation.

The documented `table.nkeys` example shows how nil assignments and mixed table regions are handled. It is copied here directly:

```lua
local nkeys = require "table.nkeys"

print(nkeys({}))  -- 0
print(nkeys({ "a", nil, "b" }))  -- 2
print(nkeys({ dog = 3, cat = 4, bird = nil }))  -- 2
print(nkeys({ "a", dog = 3, cat = 4 }))  -- 3
```

A shallow copy replaces assignment of the original table reference with assignment of a new top-level table. Nested tables are not copied by `table.clone`. The README says deep cloning is planned through a second `true` argument, but it does not present that operation as an available current API. Code should use the documented one-argument form until implementation and documentation agree on the planned behavior.

## Controlling jit.prngstate in JIT and Non-JIT Builds

`jit.prngstate` gives code access to the PRNG state used by the JIT compiler. Without an argument, it returns the current state as an array of eight Lua numbers containing 32-bit integer values. When called with a state, it sets the JIT PRNG state and returns the state that was overridden.

The accepted forms are limited. An array may contain up to eight unsigned Lua numbers, each with a value less than `2**32-1`. Missing positions are filled with zeros. For backward compatibility, the function also accepts one unsigned number below the same limit. This API controls an internal JIT compiler facility, not Lua's general random-number facilities.

The README gives the following examples:

```lua
local state = jit.prngstate()
local oldstate = jit.prngstate{ a, b, c, ... }

jit.prngstate(32) -- {32, 0, 0, 0, 0, 0, 0, 0}
jit.prngstate{432, 23, 50} -- {432, 23, 50, 0, 0, 0, 0, 0}
```

Build configuration is a hard boundary. When LuaJIT is compiled with `-DLUAJIT_DISABLE_JIT`, the README says this API has no effect and returns a table containing only zeros. Integration code must therefore check its build assumptions before treating a returned value as an active compiler PRNG state. The README does not document the intended use cases for changing that state or the concurrency rules for doing so.

## thread.exdata and the Two Pointer Slots

The `thread.exdata` module embeds one pointer value in a thread, represented on the Lua side as a cdata object with ctype `void*`. OpenResty exposes corresponding C functions named `lua_setexdata` and `lua_getexdata`. A second module, `thread.exdata2`, follows the same model but uses a separate pointer slot.

The documented Lua example is:

```lua
local th_exdata = require "thread.exdata"

th_exdata(0xdeadbeefLL)  -- set the exdata of the current Lua thread
local exdata = th_exdata()  -- fetch the exdata of the current Lua thread
```

The main thread's exdata pointer starts as `NULL`. A child Lua thread inherits the parent's value when created, but it can override it. Retrieval without an argument can be JIT compiled.

These slots require FFI. If LuaJIT is built with `-DLUAJIT_DISABLE_FFI`, the README says the API is unavailable. A second limitation is lifecycle responsibility. A pointer stored here does not acquire or define ownership through the documented API. The OpenResty README strongly discourages direct use of `thread.exdata` inside OpenResty because OpenResty core already uses it internally. That makes the interface primarily an embedding primitive rather than a convenience for ordinary application tables.

## lua_resetthread for C-Based Thread Recycling

`lua_resetthread` is a branch-specific C API intended primarily for recycling Lua threads. Its signature is:

```C
void lua_resetthread(lua_State *L, lua_State *th);
```

According to the README, this function resets `th` to the initial state of a newly created Lua thread returned by `lua_newthread()`. That distinction separates it from a function that merely runs a closure or clears selected registry entries. Its stated purpose is to restore a thread object so a host can reuse it through the thread-recycling path.

This API is relevant to embedders that already manage `lua_State` lifecycles from C. Lua-only users normally have no need for it, and libraries written entirely in Lua should not require every host to implement thread reuse around them.

The README stops immediately after describing the main use, so it does not provide a complete calling example, ownership sequence, return behavior, or post-reset state table. Those details should be checked in `doc/` and the implementation before embedding code relies on them. No repository data supports a claim that this function changes application throughput, so its value must be assessed as a state-management facility.

## Building openresty/luajit2 on Supported Toolchains

The top-level Makefile says that installation requires GNU Make and points builders to `doc/install.html` before changing variables. It describes that Makefile as suitable for POSIX platforms including Linux, BSD, and macOS. The README excerpt does not provide a complete copy-and-paste build command, so this article does not substitute an invented invocation.

Platform instructions differ. The Makefile says it is not useful for Windows with MSVC and refers those users to `src/msvcbuild.bat`. For MinGW and Cygwin, its comment directs users to change to `src` and run make with the Makefile there. The repository contains the expected source-oriented entries, including `src/`, `doc/`, `dynasm/`, `etc/`, and `t/`.

The first application-level use should be selected according to the branch APIs needed by the build. The documented table API example below can serve as a runtime check after a successful build:

```lua
local isarray = require "table.isarray"

print(isarray{"a", true, 3.14})  -- true
print(isarray{dog = 3})  -- false
print(isarray{})  -- true
```

The expected output is `true`, then `false`, then `true`. A separate build check is necessary for JIT-dependent or FFI-dependent features. `jit.prngstate` behaves differently under `-DLUAJIT_DISABLE_JIT`, and `thread.exdata` is unavailable under `-DLUAJIT_DISABLE_FFI`.

## Upgrade Boundaries, Maintenance Evidence, and the Custom LICENSE

The repository is not archived. Its last push was on 2026-10-15, and the listed release `v2.1-20250826` is dated 2025-09-14. These dates show that repository activity continued after that release, but they do not establish that every branch API is stable or that upgrading will be cost-free.

The principal upgrade risk is compatibility. An application may use `table.nkeys`, `table.clone`, `thread.exdata`, `jit.prngstate`, or a C interface without realizing that the function belongs specifically to the OpenResty branch. Moving such code to `LuaJIT/LuaJIT` can fail at build time or runtime if upstream does not expose the same interface. Embedders should review both Lua module imports and C API calls before changing the runtime branch.

The licence cannot be described as a standard open source licence from the repository metadata. GitHub labels it as Other, while the repository contains `LICENSE` and `COPYRIGHT` files. The licence clauses themselves are not reproduced in the README, so redistribution and modification terms need to be established from those files. This review does not offer legal advice or assign terms that the documentation does not state.

## Conclusion

Adopt openresty/luajit2 when software needs OpenResty's table helpers, per-thread exdata slots, PRNG state control, or thread-reset C API. Do not choose it solely on the assumption that branch-specific code will run faster or remain compatible with stock LuaJIT. Before adoption, inspect the custom LICENSE, confirm that the intended JIT and FFI settings are enabled, and verify whether existing modules use any OpenResty-only API.

## FAQ

### How do I install openresty/luajit2?

The top-level Makefile requires GNU Make and directs builders to `doc/install.html` for installation details. It also gives separate instructions for MSVC, MinGW, and Cygwin.

### Which openresty/luajit2 APIs can Lua code use?

The README documents `table.isempty`, `table.isarray`, `table.nkeys`, `table.clone`, `jit.prngstate`, `thread.exdata`, and `thread.exdata2`. The thread exdata APIs require FFI, while `jit.prngstate` has no effect when JIT is disabled.

### Will code using openresty/luajit2 work with stock LuaJIT?

Not necessarily. Code that uses the OpenResty-specific Lua or C APIs depends on a branch described by the README as distinct from upstream LuaJIT, even though the branch synchronizes upstream changes.

### What does lua_resetthread do in openresty/luajit2?

The README says it resets a thread to the initial state of a newly created thread returned by `lua_newthread()`. Its main stated purpose is Lua thread recycling.

## Sources

- [Issues](https://github.com/openresty/luajit2/issues)
- [openresty/luajit2 on GitHub](https://github.com/openresty/luajit2)
- [Project website](https://luajit.org/luajit.html)
- [README](https://github.com/openresty/luajit2/blob/v2.1-agentzh/README.md)
- [Releases](https://github.com/openresty/luajit2/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/openresty-luajit2
