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