사용자 도구

사이트 도구


kb:luapreemtivethreading

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:luapreemtivethreading [2014/11/06 17:11] (현재)
줄 1: 줄 1:
 +====== Lua Preemtive Threading ======
 +<code lua>
 +npc = create_npc("​Bob"​);​
 +npc:​set_room("​main"​);​
 +npc:​say("​Hello everybody in the room!"​);​
 +
 +SLEEP(1000); ​ -- sleep for one second
 +npc:​say("​Hello again!!"​);​
 +
 +SLEEP(1000); ​ -- sleep for another second
 +npc:​say("​I'​m being really annoying now!!"​);​
 +</​code>​
 +
 +"​SLEEP(1000)"​이라는 구문에서 의도한 바는 루아 스레드 실행을 멈추고, C/C++ 쪽으로 실행 컨트롤을 넘겨준 다음, 1초 후에 다시 루아 스레드를 실행하라는 것이다. 하지만 단순히 C 쪽의 sleep 함수를 저기다 집어넣으면,​ 루아 스레드가 C/C++ 쪽으로 컨트롤을 돌려주는 것이 아니라, 프로그램 전체가 1초 동안 먹통이 되버린다.
 +
 +기본적으로 루아 스레드는 비선점형(non-preemtive)이기 때문에, 언어 차원에서 저런 함수를 지원하지는 않는다. 그렇다고 스크립트에다 온통 yield 떡칠하는 것도 피하고 싶다. 어떻게 하면 될까...
 +
 +
 +====== 코루틴을 이용한 구현 ======
 +코루틴을 이용해 선점형 스레딩을 흉내낼 수는 있다.
 +
 +<code lua>
 +npc:​set_room("​main"​);​
 +npc:​say("​Hello everybody in the room!"​);​
 +
 +SLEEP(1000); ​ -- sleep for one second
 +npc:​say("​Hello again!!"​);​
 +
 +SLEEP(1000); ​ -- sleep for another second
 +npc:​say("​I'​m being really annoying now!!"​);​
 +</​code>​
 +
 +위 코드에서 쓰이는 SLEEP 함수는 다음과 같이 정의한다. ​
 +
 +<code lua>
 +function SLEEP(duration)
 +    coroutine.yield(duration)
 +end
 +</​code>​
 +
 +즉 SLEEP 호출이 일어나면 coroutine.yield를 수행하되,​ 몇 초 후에 스크립트의 실행을 재개해야 하는지를 C/C++ 쪽으로 넘긴다. ​
 +
 +C/C++ 쪽에서는 lua_newthread 함수를 이용해 스레드를 생성하고,​ on_npc_create 함수를 실행할 스레드 메인 함수로 지정한 다음, lua_resume 함수를 통해 실행을 재개한다. lua_resume의 반환값에는 3가지 경우가 있다.
 +
 +^ 실행 ^ 리턴값 ^ 스택에 존재하는 값 ^
 +| 끝난 경우 | 0 | 스레드 메인 함수가 반환한 값 |
 +| 중단된 경우 | LUA_YIELD (1) | lua_yield 함수에다 넘긴 인수들 |
 +| 에러가 생긴 경우 | not 0 or 1 | 에러 메시지 |
 +
 +:!: 예전 버전에서는 끝난 경우 및 중단된 경우 모두 0이 아닌 값을 반환했었던 걸로 기억하는데,​ 어느샌가 LUA_YIELD라는 값이 생겼다. 은근슬쩍 바뀌는 거 짜증나...
 +
 +<code cpp>
 +lua_State* prepare(lua_State* L, const char* filename)
 +{
 +    lua_State* coroutine = lua_newthread(L);​
 +    if (luaL_loadfile(coroutine,​ filename))
 +    {
 +        // 파싱 실패! 여기서 에러 처리...
 +        ...
 +        return NULL;
 +    }
 +
 +    return coroutine;
 +}
 +
 +enum ResultCode
 +{
 +    FINISHED,
 +    SUSPENDED,
 +    ERROR_OCCURED
 +}
 +
 +std::​pair<​ResultCode,​ int> execute(lua_State* coroutine)
 +{
 +    int result = lua_resume(coroutine,​ 0);
 +    if (result == 0) // 실행 완료.
 +    {
 +        return std::​make_pair<​ResultCode,​ int>​(FINISHED,​ 0);
 +    }
 +    else if (result != LUA_YIELD) // 실행 중단.
 +    {
 +        int duration = 0;
 +        int duration = (int)lua_tonumber(coroutine,​ -1);
 +        lua_pop(coroutine,​ 1);
 +        return std::​make_pair<​ResultCode,​ int>​(SUSPENDED,​ duration);
 +    }
 +    else // 에러 발생.
 +    {
 +        return std::​make_pair<​ResultCode,​ int>​(ERROR_OCCURED,​ -1);
 +    }
 +}
 +
 +</​code>​
 +
 +execute 함수의 반환값에 따라, C/C++ 쪽에서 처리를 해줘야한다. 만일 기다려야 한다면, 폴링이 되었든, 타이머를 이용하든,​ 주어진 시간만큼 기다린 후에, 다시 execute를 호출하면 된다. 언젠가는 완료되겠지...
 +
 +<code cpp>
 +int main()
 +{
 +    ...
 +
 +    lua_State* t = prepare(L, "​test.lua"​);​
 +    int next_time = 0;
 +
 +    while (t != NULL)
 +    {
 +        if (next_time < get.current.time())
 +        {
 +            std::​pair<​ResultCode,​ int> result = execute(t);
 +            if (result.first == SUSPENDED) // 중단 처리.
 +            {
 +                // 주어진 값만큼 시간이 지난 후에 다시 실행한다.
 +                next_time = get.current.time() + result.second;​
 +            }
 +            else if (result.first == FINISHED) // 완료 처리.
 +            {
 +                t = NULL;
 +            }
 +            else // 에러 처리.
 +            {
 +                t = NULL;
 +            }
 +        }
 +    }
 +
 +    ...
 +
 +    return 0;
 +}
 +</​code>​
 +
 +이 방법의 단점은 C/C++ 쪽에서 처리하는 것이 너무 많다는 것이다. 뭔가 루아스럽지않다고 해야 하나? 루아스러운게 뭔지도 잘 모르겠다만 어쨌든 찝찝하다는 느낌을 버릴 수 없다.
 +
 +
 +====== 훅을 이용한 구현 ======
 +[[ToBeFilled]]
 +
 +  * [[http://​www.tecgraf.puc-rio.br/​~lhf/​ftp/​lua/​]]
 +  * [[http://​www.icynorth.com/​forums/​viewtopic.php?​p=228&​sid=24c427141b80232a86258d2e41c6b0ee#​228]]
 +  * [[http://​lua-users.org/​lists/​lua-l/​2006-01/​msg00002.html]]
 +  * [[http://​lua-users.org/​lists/​lua-l/​2005-12/​threads.html#​00340]]
 +  * [[http://​lua-users.org/​cgi-bin/​namazu.cgi?​query=parallel&​sort=score&​idxname=lua-l&​max=20&​whence=40]]
 +  * [[http://​lua-users.org/​lists/​lua-l/​2004-01/​msg00369.html]]
 +
 +----
 +  * see also [[Lua]]
  
kb/luapreemtivethreading.txt · 마지막으로 수정됨: 2014/11/06 17:11 (바깥 편집)