7. 소스 설명


   7.1 poll 객체 관리 구조체


     poll_t 구조체는 사용자 데이터 형으로 poll을 구현하기 위한
     구조체이다.


typedef struct 
{
    int     max_count;
    struct  pollfd     *ufds;
    
    int     count;
    int     wait_time;
    int     poll_result;
    
} poll_t;

    이 구조체는 C 함수 인 poll()함수에 필요한 파라메터 및 관리를 위한 필드로 구성된다.
     
     poll 객체는 내부적으로 최대로 관리할수 있는 폴이벤트 객체수를 다음 필드로 정의 하고 있다. 


int     max_count;


    이 값은 poll_create() 함수의 인자로써 전달된다.


    실제로 등록된 이벤트 객체는


int     count;


     에 관리 된다. 


     poll() 함수를 호출하기 위한 조건중 시간 초과 값은


 int     wait_time;

     로 관리 되며 poll() 함수 실행 결과는


 int     poll_result;

     에 남게 된다.



   7.2 제어 대상이 되는 poll 객체 생성 지원 c 함수


     루아에서는 poll 객체를 사용하려면 다음과 같이 poll 객체를 생성해야 한다.


        p = require "poll"; -- 폴 모듈 생성
        poll = p.create(4); -- poll 객체 생성


     이 스크립트 문장은 다음과 같이 정의된  poll 객체 모듈에서 등록된 함수 poll_create()를 호출하게 된다. 


 int poll_create( lua_State *L )
{
    poll_t *pol;
    
    pol = poll_new_userdata(L);

    pol->max_count = lua_tointeger( L, 1 );
    if( pol->max_count < 0 ) pol->max_count = 0;

    if( pol->max_count == 0 )   pol->ufds = NULL;
    else                        pol->ufds = malloc( pol->max_count * sizeof( struct pollfd ) ); 

    pol->count         = 0;
    pol->wait_time     = 0;
    
    return 1;
}

static const struct luaL_Reg pm[] =
{
    { "create"      , poll_create     },
    { NULL, NULL }
};

     poll_create() 함수는     poll_new_userdata() 함수를 이용하여 사용자 데이터를 생성하고 필요로 하는
     이벤트 관리 구조체인 pol->ufds 에 메모리를 할당 한 후 디폴트 값을 넣는다.


     poll_new_userdata() 함수는 다음과 같이 정의되어 있다.


 static poll_t *poll_new_userdata(lua_State *L)
{
    poll_t *pol = (poll_t *) lua_newuserdata(L, sizeof(poll_t));
                            
    luaL_getmetatable(L, POLL);
    lua_setmetatable(L, -2);
    return pol;
}

     lua_newuserdata() 함수를 사용하여 poll_t 구조체를 다룰 수 있는 사용자 데이터를 할당하고
     POLL 메타테이블을 연결한다.
   
     POLL 메타 테이블은 다음과 같은 구성을 갖는다.


static const luaL_reg poll_meta[] = {
      {"__index"              , poll_index                }, // poll 객체의 멤버 변수 값 참조                   
      {"__tostring"           , poll_tostring             }, // poll 객체의 문자열 반환                         
      {"__gc"                 , poll_gc                   }, // poll 객체의 소멸시 처리해야 할 루틴             
      {"clear"                , poll_clear                }, // poll 객체의 이벤트 초기화
      {"wait"                 , poll_wait                 }, // poll 객체의 이벤트 대기
      {"check_event_timeout"  , poll_check_event_timeout  }, // poll 객체의 시간 초과 체크
      {"add_event"            , poll_add_event            }, // poll 객체의 이벤트 추가
      {"check_event_read"     , poll_check_event_read     }, // poll 객체의 읽기 이벤트 발생 체크
      {"check_event_error"    , poll_check_event_error    }, // poll 객체의 에러 이벤트 발생 체크
      {"check_event_write"    , poll_check_event_write    }, // poll 객체의 쓰기 이벤트 발생 체크
      {0, 0}
};

     이 메타 테이블은 poll_new_metatable() 함수에 의해서 루아 스테이지에 등록된다.
   
     poll_new_metatable() 함수는  poll 모듈이 등록될때 호출되는 luaopen_poll() 에서 호출한다.



   7.3 메타 테이블 함수가 호출될때 사용자 데이터를 얻어 오는 C 함수


     메타 테이블에 지정된 함수가 호출되면 다음과 같이 poll_t 구조체 데이터 주소를 얻어 와야 한다.


poll_t *pol;
    
pol = poll_get_data( L, 1 );

    poll_get_data() 함수는 다음과 같이 정의 되어 있다.


static poll_t * poll_get_data(lua_State *L, int index)
{
      poll_t *pol = (poll_t *) lua_touserdata(L, index);
      if (pol == NULL) luaL_typerror(L, index, POLL);
      return pol;
}

     객체 멤버 변수가 참조되거나 함수가 호출되면 사용자 데이터는 스택 처음에 놓이기 되는데
     이것을 얻어와 형변환 하여 반환한다.



   8.4 객체 함수 호출 사전 처리


     poll 객체는 함수를 호출하게 되면 __index 메타 함수인 poll_index() 를 호출하게 된다.

     poll_index() 함수는 함수 호출이면 사용자 데이터를 전역 변수 call_func_poll 에 넣어

     이후에 메타 테이블에 정의된 함수가 호출되었을때 사용되도록
     만든다.


     poll_index() 함수는 다음과 같이 구현되어 있다.


static int poll_index( lua_State *L )
{
    poll_t     *pol;
    char     *var_str; 
    
    if( lua_type(L, 2) == LUA_TSTRING ) 
    {
        pol         = poll_get_data( L, 1 );
        
        var_str = (char *) lua_tostring( L, 2 );          

        if     ( !strcmp( var_str, "wait"                   ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "clear"                  ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_timeout"    ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "add_event"              ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_read"       ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_error"      ) ) { call_func_pol = pol; }
        else if( !strcmp( var_str, "check_event_write"      ) ) { call_func_pol = pol; }

        lua_getmetatable(L, 1);
        lua_replace(L, 1);
        lua_rawget(L, 1);
        return 1;
    }
        
    return 0;
}


   7.5 이벤트 등록 전 초기화


     poll 객체는 이벤트를 등록하기 이전에 내부적인 관리 버퍼를 초기화 하여야 한다.
     이것을 처리하는 것이 poll_clear() 함수이다. 다음과 같이 구현되어 있다.


 static int poll_clear(lua_State *L)
{
    
    poll_t             *pol;
    struct     pollfd    *poll_item;
    int                lp;
    
    pol = call_func_pol;
    
    pol->count = 0;
    
    call_func_pol = NULL;
    
    if( pol->max_count     == 0    ) return 0; 
    if( pol->ufds       == NULL ) return 0; 
    
    memset( pol->ufds, 0, sizeof( struct pollfd ) * pol->max_count );
    poll_item = pol->ufds;
    
    for( lp = 0; lp < pol->max_count; lp++ ) 
    {
        poll_item->fd = -1;
        poll_item++;
    }
    
    return 0;
}

     pol->count 변수를 0으로 초기화 하고 pol->ufds 에 관리되고 있는 이벤트를 모두 초기화 한다.



   7.6 이벤트 등록


     이벤트는 pol->ufds 에 추가 형식으로 등록된다. 즉 삭제에 대한 고려가 되어 있지 않다.
     이벤트 등록 처리는 poll_add_event() 가 수행한다. 다음과 같이 구현되어 있다.


static int poll_add_event(lua_State *L)
{

    poll_t             *pol;
    struct     pollfd    *poll_item;
    int                lp;
    int                append_fd;
    int                append_read;
    int                append_error;
    int                append_write;

    pol = call_func_pol;

    //    printf( "CALL poll_add_event\n" );

    append_fd   = lua_tointeger( L, 1 );
    append_read = lua_toboolean( L, 2 );
    append_error= lua_toboolean( L, 3 );
    append_write= lua_toboolean( L, 4 );

    if( pol->max_count  == 0            ) return 0; 
    if( pol->ufds       == NULL         ) return 0; 
    if( pol->max_count  <= pol->count   ) return 0;
    
    poll_item = pol->ufds;    
    for( lp = 0; lp < pol->max_count; lp++ ) 
    {
        if( poll_item->fd == append_fd )
        {
        printf( "error - same fd %d\n", append_fd );
        break;
        }    
        
        if( poll_item->fd == -1 )
        {
            poll_item->fd = append_fd;
            poll_item->events     = 0;
            poll_item->revents    = 0;
            if( append_read     ) poll_item->events |= POLLIN;
            if( append_error    ) poll_item->events |= POLLERR;
            if( append_write    ) poll_item->events |= POLLOUT;
            
            if( poll_item->events == 0 )
            {
                poll_item->fd = -1;
                printf( "error - append without event\n" );
            }
            else
            {
                pol->count = pol->count + 1;    
                //  printf( "append ok fd = %d count = %d\n", poll_item->fd  , pol->count );
            }        
        
            break;
        }
        poll_item++;
    }

    call_func_pol = NULL;
    
    return 0;
}

     추가될 이벤트 대상 파일 디스크립터와 이벤트 종류를 구한 후
     가장 먼저 동일한 fd 에 대한 등록이 되어 있는가를 검사 한다.
     만약 없다면 pol->ufds에 추가 하고 pol->count를 증가 시킨다.



   7.7  이벤트 대기 구현


     등록된 이벤트가 발생하기를 기다리는 것은 poll_wait() 에서 구현하는데 이 함수에서 C 라이브러리의 poll() 함수를
     호출한다. 다음과 같이 구현한다.


 static int poll_wait(lua_State *L)
{

    poll_t     *pol;
    int        ret;
    
    pol = call_func_pol;
    
    pol->poll_result = -1;
    
    ret = -1;
    
    pol->wait_time = lua_tointeger( L, 1 );
    
    pol->poll_result = poll( pol->ufds, pol->count, pol->wait_time );
    
    if( pol->poll_result >= 0 ) lua_pushboolean(L, 1);
    else                        lua_pushboolean(L, 0);
    
    call_func_pol = NULL;
    
    return 1;    

}

     대기 시간만 파라메터 인자로 구하고 나머지는 사용자 데이터인 poll_t 구조체를 그대로 사용한다.
     poll() 함수의 결과는 pol->poll_result에 담겨 진다.


   7.8 시간 초과 체크와 이벤트 체크


     아무런 이벤트가 발생하지 않으면 C 라이브러리의 poll() 함수는 0을 반환하는데
     이값은 pol->poll_result 에 기록되어 있다.

     시간 초과가 발생했는지를 검사 하는 함수는 poll_check_event_timeout() 인데
     이 함수는 단순히 pol->poll_result 값이 0 이면 참값을 아니면 거짓값을 반환
     하게 한다.

     이벤트 체크는 결과가

     pol->ufds[n]revents 에 개개의 fd 에 대한 결과가 기록된다.
     pol->ufds[n]fd이 값과 함수의 파라메터로 전달된 fd 와 일치했을때 각 이벤트의
     비트 필드 속성을 조사하여 이벤트가 발생한 상태이면 참값을 그렇지 않으면 거짓값을
     반환한다.


C000_lua.gif