[ EFL 게시물 목차 : http://yellowbirds.tistory.com/1 ]
안녕하세요? 천재태지 서주영입니다.ecore_timer 는 EFL 에서 사용하는 타이머입니다.
타이머에 원하는 주기와 콜백을 설정해놓으면 매 주기마다 콜백이 불리게 됩니다. 타이머는 어플리케이션 제작 시에 흔히 사용하기 때문에 EFL 에서도 타이머를 제공합니다.
timer 를 생성하는 함수는 아래와 같습니다.
EAPI Ecore_Timer *ecore_timer_add(double in,Ecore_Task_Cb func,const void *data)
[유의사항 1 - 타이머의 주기]
기본적으로는 정해놓은 주기마다 콜백이 불리지만, 한가지 유의해야할 점은, 정확하게 해당 주기 마다 타이머가 불리는 것이 아니라 해당 주기의 시간이 초과했을 때 콜백이 불린다는 겁니다. 즉, 주기를 1.0 초로 설정해놓으면 최초 시간의 1.01 혹은 1.02 초 뒤에 콜백이 불릴 수 있다는 것입니다. 이 점을 잘 고려해서 사용해야 합니다.
[유의사항 2 - 타이머 종료]
타이머를 종료하는 방법은 두 가지가 있습니다.
첫번째는 ecore_timer_del() 을 이용하는 방법이고 두번째는 타이머에 걸어둔 콜백에서 ECORE_CALLBACK_CANCEL 을 리턴하는 방법입니다.
ecore_timer_del() 은 원하는 시점에 타이머를 종료하는 방법인데, 이 함수의 원형은 아래와 같습니다.
EAPI void *ecore_timer_del(Ecore_Timer *timer)
그리고 타이머 콜백에서 ECORE_CALLBACK_CANCEL 을 리턴하는 경우는 타이머를 더이상 사용하지 않고 폐기시키겠다는 의미입니다. 만약 타이머를 계속 돌리고 싶다면, ECORE_CALLBACK_RENEW 를 리턴하시면 됩니다.
이 두가지 용법을 알고 상황에 맞게 적절하게 사용하셔야 합니다.
유의사항 3 - 타이머 변수 처리)
이번 유의사항이 가장 중요한 겁니다. 타이머에 대한 포인터를 변수에 저장해놓을 때, 타이머를 삭제한 경우 타이머 포인터를 NULL 로 초기화해줘야 합니다.
예를 들어, 아래와 같은 코드가 있다고 했을 때
Ecore_Timer *my_timer = ecore_timer_add(1.0, _my_timer_cb, NULL);ecore_timer_del() 을 실행하는 순간 타이머가 종료됩니다.
...
...
if (my_timer)
ecore_timer_del(my_timer);
그런데 만약 ecore_timer_del() 을 한 뒤에 다시 my_timer 를 체크하고 ecore_timer_del() 을 실행하면 어떻게 될까요?
Ecore_Timer *my_timer = ecore_timer_add(1.0, _my_timer_cb, NULL);얼핏 생각하면 이미 타이머가 종료되었기 때문에 ecore_timer_del() 은 아무것도 하지 않을 것 같지만, 실제로는 아래와 같은 에러 메시지를 출력합니다.
...
...
if (my_timer)
ecore_timer_del(my_timer); ---> (1)
...
...
if (my_timer) ---> (2)
ecore_timer_del(my_timer);
ERR<6750>:ecore ecore.c:441 _ecore_magic_fail()*** ECORE ERROR: Ecore Magic Check Failed!!!*** IN FUNCTION: ecore_timer_del()ERR<6750>:ecore ecore.c:451 _ecore_magic_fail() Input handle is wrong typeExpected: f7d713f4 - Ecore_Timer (Timer)Supplied: 008c2dc1 - <UNKNOWN>ERR<6750>:ecore ecore.c:454 _ecore_magic_fail() *** NAUGHTY PROGRAMMER!!!*** SPANK SPANK SPANK!!!*** Now go fix your code. Tut tut tut!
아주 자극적인 메시지 입니다. EFL 의 창시자인 Rasterman 의 말에 따르면 '자극을 받아서 어플리케이션을 고치도록 이런 메시지를 만들었다!'라고 합니다. 이 메시지를 보면 기분이 언짢아지겠죠...? 일반적인 개발자라면 메시지를 보고 원인을 찾아보고 문제를 해결하려고 하지만, 신기하게도 대부분 이런 경고를 무시합니다.
이 문제가 발생한 원인은, 위 코드에서 (1) 번 과정에서 my_timer 가 가리키는 타이머 자체는 종료시켰지만, 어플리케이션에서 로컬에 저장하고 있는 my_timer 라는 포인터 변수의 값은 그대로 있기 때문입니다. 즉, 타이머를 종료시킨 후에는 my_timer 가 쓸데없는 곳을 가리키게 됩니다. 참고로 이를 dangling pointer 라고 합니다.
그렇기 때문에 (2) 번 과정에서 my_timer 에 쓸데없는 값일지라도 값이 있기 때문에 조건문 안으로 들어가서 ecore_timer_del() 을 수행하게 됩니다. 그런데 my_timer 가 가리키는 곳에는 타이머가 없기 때문에 위와 같은 에러 메시지를 출력합니다.
그러므로 타이머를 종료할 때는 타이머를 가리키는 변수를 항상 NULL 로 초기화해야 합니다.
이 문제가 발생한 원인은, 위 코드에서 (1) 번 과정에서 my_timer 가 가리키는 타이머 자체는 종료시켰지만, 어플리케이션에서 로컬에 저장하고 있는 my_timer 라는 포인터 변수의 값은 그대로 있기 때문입니다. 즉, 타이머를 종료시킨 후에는 my_timer 가 쓸데없는 곳을 가리키게 됩니다. 참고로 이를 dangling pointer 라고 합니다.
그렇기 때문에 (2) 번 과정에서 my_timer 에 쓸데없는 값일지라도 값이 있기 때문에 조건문 안으로 들어가서 ecore_timer_del() 을 수행하게 됩니다. 그런데 my_timer 가 가리키는 곳에는 타이머가 없기 때문에 위와 같은 에러 메시지를 출력합니다.
그러므로 타이머를 종료할 때는 타이머를 가리키는 변수를 항상 NULL 로 초기화해야 합니다.
그리고 ecore_timer_del() 이 아니라 콜백의 리턴값을 이용하여 타이머를 종료할 때도 타이머를 가리키는 변수를 항상 NULL 로 초기화해야 합니다.Ecore_Timer *my_timer = ecore_timer_add();...
...
if (my_timer)
{
ecore_timer_del(my_timer);
my_timer = NULL;
}
Eina_Bool이는 비단 EFL 의 문제가 아니라 C 언어를 사용하는 프로그램에서 발생하는 근본적인 문제입니다. 그러므로 앞으로는 이런 종류의 상황에 접했을 때 변수를 적절하게 초기화하는 센스를 발휘하기 바랍니다.
_my_timer_cb(void *data)
{
my_timer = NULL;
return ECORE_CALLBACK_CANCEL;
}
감사합니다.
[ EFL 게시물 목차 : http://yellowbirds.tistory.com/1 ]
'EFL, Enlightenment' 카테고리의 다른 글
[EFL 소식] EFL 1.1/1.5 베타 배포 (2011.11.28) (0) | 2011.11.30 |
---|---|
[EFL 소식] EFL 1.1/1.5 알파 배포 (2011.11.16) (0) | 2011.11.26 |
[EFL] elementary 라이브러리에서 els, elc, elm, elu 란? (0) | 2011.10.14 |
[EFL] 최신 EFL 소스코드 다운로드 (0) | 2011.10.13 |
[블로그 번역] Enlightenment, DR17, EFL 이란? (0) | 2011.10.12 |