Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference behind normal function call and pcall

Tags:

lua

I am using lua and I know pcall is for protected calling and my question is if both ways of calling all come down to the same C code. e.g.

function a(arg)
   ...
end

normal calling:

a(arg)

protected call:

pcall(a, arg)

Actually I am using 'lua_lock/lua_unlock' to protect the lua_State from corrupting. And form the source (lua 5.1.4) I can see 'lua_pcall' is calling 'lua_lock/lua_unlock', but I am not sure if normal way of function call is also based on 'lua_pcall' or using 'lua_lock/lua_unlock'? If not, does it mean that I have to change all the function calling to 'pcall(...)' to get benefit from 'lua_lock/lua_unlock'?

Could someone explain? Thank you

like image 604
Mickey Shine Avatar asked May 20 '13 03:05

Mickey Shine


1 Answers

pcall is used to handle errors in lua. I've made the following example to demonstrate how to use it:

First we make a function which me know will produce an error

function makeError(n)
    return 'N'+n;
end 

Now as our first example we define the following

function pcallExample1()
    if pcall(makeError,n) then 
        print("no error!")
    else 
        print("That method is broken, fix it!")
    end 
end 

We invoke pcallExample1

pcallExample1()

And get the output:

Lua 5.1.3  Copyright (C) 1994-2008 Lua.org, PUC-Rio
That method is broken, fix it!

To demonstrate the opposite:

function pcallExample2()
    if makeError(5) then 
        print("no error!")
    else 
        print("That method is broken, fix it!")
    end 
end 

Invoking this will have the error remain unhanded and bubble up to the display:

lua: /Users/henryhollinworth/Desktop/s.lua:2: attempt to perform arithmetic on a string value

In terms of C, pcall is defined as

static int luaB_pcall (lua_State *L) {
  int status;
  luaL_checkany(L, 1);
  lua_pushnil(L);
  lua_insert(L, 1);  /* create space for status result */
  status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, pcallcont);
  return finishpcall(L, (status == LUA_OK));
}

Where lua_pcallk is

LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
                        int ctx, lua_CFunction k) {
  struct CallS c;
  int status;
  ptrdiff_t func;
  lua_lock(L);
  api_check(L, k == NULL || !isLua(L->ci),
    "cannot use continuations inside hooks");
  api_checknelems(L, nargs+1);
  api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
  checkresults(L, nargs, nresults);
  if (errfunc == 0)
    func = 0;
  else {
    StkId o = index2addr(L, errfunc);
    api_checkvalidindex(L, o);
    func = savestack(L, o);
  }
  c.func = L->top - (nargs+1);  /* function to be called */
  if (k == NULL || L->nny > 0) {  /* no continuation or no yieldable? */
    c.nresults = nresults;  /* do a 'conventional' protected call */
    status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
  }
  else {  /* prepare continuation (call is already protected by 'resume') */
    CallInfo *ci = L->ci;
    ci->u.c.k = k;  /* save continuation */
    ci->u.c.ctx = ctx;  /* save context */
    /* save information for error recovery */
    ci->u.c.extra = savestack(L, c.func);
    ci->u.c.old_allowhook = L->allowhook;
    ci->u.c.old_errfunc = L->errfunc;
    L->errfunc = func;
    /* mark that function may do error recovery */
    ci->callstatus |= CIST_YPCALL;
    luaD_call(L, c.func, nresults, 1);  /* do the call */
    ci->callstatus &= ~CIST_YPCALL;
    L->errfunc = ci->u.c.old_errfunc;
    status = LUA_OK;  /* if it is here, there were no errors */
  }
  adjustresults(L, nresults);
  lua_unlock(L);
  return status;
}

In contrast to lua_callk

LUA_API void lua_callk (lua_State *L, int nargs, int nresults, int ctx,
                        lua_CFunction k) {
  StkId func;
  lua_lock(L);
  api_check(L, k == NULL || !isLua(L->ci),
    "cannot use continuations inside hooks");
  api_checknelems(L, nargs+1);
  api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
  checkresults(L, nargs, nresults);
  func = L->top - (nargs+1);
  if (k != NULL && L->nny == 0) {  /* need to prepare continuation? */
    L->ci->u.c.k = k;  /* save continuation */
    L->ci->u.c.ctx = ctx;  /* save context */
    luaD_call(L, func, nresults, 1);  /* do the call */
  }
  else  /* no continuation or no yieldable */
    luaD_call(L, func, nresults, 0);  /* just do the call */
  adjustresults(L, nresults);
  lua_unlock(L);
}

Note that both make use of lua_lock() and lua_unlock(). Both lock and unlock the lua_State.

like image 132
HennyH Avatar answered Oct 22 '22 09:10

HennyH