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 와 일치했을때 각 이벤트의
비트 필드 속성을 조사하여 이벤트가 발생한 상태이면 참값을 그렇지 않으면 거짓값을
반환한다.


