tarantool / tarantool/tarantool

ERROR in finalizer: ?

Open
#10,695 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Lua
Stars
3.7k
Forks
419
Avg merge
1d 23h
Merged PRs (30d)
88

Description

Problem statement

On testing of a private module with a significant amount of code I got the following output at end of 5 minutes of tests[^1]:

ERROR in finalizer: ?

The error message has a question, but I have a question too: how to understand which finalizer fails?

My way to answer is to ask a LuaJIT wizard @Buristan, which performs some gdb magic and points the needed finalizer. The problem is that Sergey is a human and we can't scale him. We need other ways to debug the finalizers.

Investigation

Let's create a minimal reproducer:

#!/usr/bin/env tarantool

local function myerror(msg)
    local err = newproxy(true)
    getmetatable(err).__tostring = function()
        return msg
    end
    return err
end

local ud = newproxy(true)
getmetatable(ud).__gc = function()
    error(myerror('my error message'))
end

ud = nil
collectgarbage()

Run it using tarantool (3.3.0-entrypoint-160-g11145f6a82):

$ tarantool t.lua 
ERROR in finalizer: ?

Run it using LuaJIT (2.1.0-beta3):

$ luajit t.lua
luajit: my error message
stack traceback:
	[C]: in function 'error'
	t2.lua:13: in function <t2.lua:12>
	[C]: in function 'collectgarbage'
	t2.lua:17: in main chunk
	[C]: at 0x55fc2665bfe0

The code that prints the error is the following:

static int error_finalizer(lua_State *L)
{
  const char *s = lua_tostring(L, -1);
  fputs("ERROR in finalizer: ", stderr);
  fputs(s ? s : "?", stderr);
  fputc('\n', stderr);
  fflush(stderr);
  return 0;
}

Note that lua_tostring() doesn't call the __tostring metamethod. It looks like tarantool and LuaJIT both should print ?, but LuaJIT surprisingly shows the error message.

The reason (pointed to me by @Buristan) is that luajit executable (unlike the LuaJIT library) has the following top-level exception handler:

static int traceback(lua_State *L)
{
  if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */
    if (lua_isnoneornil(L, 1) ||
	!luaL_callmeta(L, 1, "__tostring") ||
	!lua_isstring(L, -1))
      return 1;  /* Return non-string error object. */
    lua_remove(L, 1);  /* Replace object by result of __tostring metamethod. */
  }
  luaL_traceback(L, L, lua_tostring(L, 1), 1);
  return 1;
}

It calls the __tostring metamethod (and adds the traceback to the error message).

[^1]: The issue in the module: https://github.com/tarantool/etcd-client/issues/88.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/lib_aux.c#error_finalizer and src/luajit.c#traceback, then run the minimal t.lua reproducer with Tarantool and LuaJIT. Trace how finalizer errors reach the handler and compare the resulting diagnostics. Done means the failing finalizer can be identified from the emitted error output rather than showing only “?”.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, lua
Domain
backend, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.