PDA

Просмотр полной версии : [Function] dc_SleepFix



Daniel_Cortez
13.01.2016, 17:24
ВНИМАНИЕ: данная функция всё ещё не работает нигде, кроме функции main.
В ней только исправлена ошибка с неправильными значениями в указателе вершины стека (stack/heap collision), возникающая при очень частом использовании оператора sleep.

Скорее всего, вам эта функция нигде не пригодится, но я на всякий случай задокументирую её здесь.
Возможное использование: проведение тестов, в которых оператор sleep используется очень часто (в циклах, например). Мне хватило 2037 раз, чтобы вызвать ошибку времени выполнения обычным sleep - при использовании dc_SleepFix её нет.
Один из тестов можно найти здесь: http://pro-pawn.ru/showthread.php?13189



dc_SleepFix(time) // by Daniel_Cortez \\ pro-pawn.ru
{ // WARNING: This function is still only usable in main().
// It just fixes the stack/heap collision error in default sleep().
static heap_ptr, stack_ptr;
#emit lctrl 2
#emit stor.pri heap_ptr
#emit lctrl 4
#emit stor.pri stack_ptr
sleep(time);
#emit load.pri stack_ptr
#emit sctrl 4
#emit load.pri heap_ptr
#emit sctrl 2
}
#if defined _ALS_sleep
#undef sleep
#else
#define _ALS_sleep
#endif
#define sleep(%0) dc_SleepFix(%0)



Автор: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)

Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Копирование данной статьи на других ресурсах без разрешения автора запрещено!

ziggi
13.01.2016, 18:19
Вариант из fixes от Y_Less:


/*
* FIXES_sleep(const time)
*
* FIXES:
* sleep
*/

// Uses a little trick to consume part of the line and thus not match
// our hooked version.
#if defined _ALS_sleep
#error _ALS_sleep defined
#endif
#define BAD_sleep%0\n%9 sleep%0

#if FIX_sleep
stock FIXES_sleep(ms)
{
// Call a native function that does very little, but saves the current
// heap pointer. Then return to save the accurate stack pointer.
return heapspace(), ms;
}

#define _ALS_sleep

#define sleep%0\n%9 sleep FIXES_sleep(%0)
// This fixes another BIZZARE bug. Just doing:
//
// #define FIXES_sleep(%0;) FIXES_sleep(%0)
//
// Results in:
//
// FIXES_sleep(n));
//
// Which clearly it shouldn't. I've stepped through the compilation and that
// extra bracket comes from nowhere!
#define FIXES_sleep(%0;) FIXES_sleep _FIXES_SLEEP_BRACKET %0);
#define _FIXES_SLEEP_BRACKET (
#endif

Daniel_Cortez
13.01.2016, 19:02
Вариант из fixes от Y_Less:


/*
* FIXES_sleep(const time)
*
* FIXES:
* sleep
*/

// Uses a little trick to consume part of the line and thus not match
// our hooked version.
#if defined _ALS_sleep
#error _ALS_sleep defined
#endif
#define BAD_sleep%0\n%9 sleep%0

#if FIX_sleep
stock FIXES_sleep(ms)
{
// Call a native function that does very little, but saves the current
// heap pointer. Then return to save the accurate stack pointer.
return heapspace(), ms;
}

#define _ALS_sleep

#define sleep%0\n%9 sleep FIXES_sleep(%0)
// This fixes another BIZZARE bug. Just doing:
//
// #define FIXES_sleep(%0;) FIXES_sleep(%0)
//
// Results in:
//
// FIXES_sleep(n));
//
// Which clearly it shouldn't. I've stepped through the compilation and that
// extra bracket comes from nowhere!
#define FIXES_sleep(%0;) FIXES_sleep _FIXES_SLEEP_BRACKET %0);
#define _FIXES_SLEEP_BRACKET (
#endif

Да, я видел этот вариант, но он меня не устроил.

В комментарии можно увидеть, что автор фикса (Y_Less) утверждает, якобы нативная функция (heapspace) сохраняет текущий указатель кучи ("Call a native function that does very little, but saves the current heap pointer.").
Интересно, где он это увидел? Код функции можно посмотреть здесь (https://github.com/Zeex/pawn/blob/master/source/amx/amxcore.c#L188). Всё, что делает функция - возвращает разницу между значениями в указателях стека и кучи, т.е. свободное пространство в секции стека/кучи. Никакого сохранения указателей стека, кучи или фрейма стека в ней нет.
По сути оператор sleep заменяется на какую-то функцию, которая даже не производит никакой приостановки выполнения кода. С таким же успехом можно заменить sleep на функцию-пустышку.


stock FIX_Sleep(ms) return ms;

В чём смысл такого "фикса"? Можно ли это вообще назвать "фиксом"? Удивительно, почему этого до сих пор никто не заметил - вроде бы среди заморской аудитории много кто пользуется этим инклудом.

ziggi
13.01.2016, 19:25
Да, я видел этот вариант, но он меня не устроил.

В комментарии можно увидеть, что автор фикса (Y_Less) утверждает, якобы нативная функция (heapspace) сохраняет текущий указатель кучи ("Call a native function that does very little, but saves the current heap pointer.").
Интересно, где он это увидел? Код функции можно посмотреть здесь (https://github.com/Zeex/pawn/blob/master/source/amx/amxcore.c#L188). Всё, что делает функция - возвращает разницу между значениями в указателях стека и кучи, т.е. свободное пространство в секции стека/кучи. Никакого сохранения указателей стека, кучи или фрейма стека в ней нет.
По сути оператор sleep заменяется на какую-то функцию, которая даже не производит никакой приостановки выполнения кода. С таким же успехом можно заменить sleep на функцию-пустышку.


stock FIX_Sleep(ms) return ms;

В чём смысл такого "фикса"? Можно ли это вообще назвать "фиксом"? Удивительно, почему этого до сих пор никто не заметил - вроде бы среди заморской аудитории много кто пользуется этим инклудом.

Хм, и правда, спасибо. В ближайшее время обновлю свой форк этой либы.

Daniel_Cortez
13.01.2016, 19:29
Хм, и правда, спасибо. В ближайшее время обновлю свой форк этой либы.
ИМХО, вряд ли в SA:MP есть хоть что-то, вызывающее задержку без вызова коллбэка (т.е. в пределах вызывающей функции) и без создания нагрузки на процессор. В конце концов, он для того и создан, чтобы быть однопоточным. Но если всё же будут идеи, постараюсь сделать PR в твоём форке.

ziggi
13.01.2016, 19:48
Стоп. Я протестировал стандартную функцию, хватило примерно 1300 вызовов для получения этой ошибки. Затем я взял фикс от Y_Less и он, походу, действительно работает, ибо на 27 тысячах итераций полёт нормальный.

Тестировал очень просто:


main()
{
new a;
while (++a) {
printf("%d", a);
sleep(0);
}
}

Daniel_Cortez
13.01.2016, 20:00
Стоп. Я протестировал стандартную функцию, хватило примерно 1300 вызовов для получения этой ошибки. Затем я взял фикс от Y_Less и он, походу, действительно работает, ибо на 27 тысячах итераций полёт нормальный.

Тестировал очень просто:


main()
{
new a;
while (++a) {
printf("%d", a);
sleep(0);
}
}

Действительно, стоп. А теперь назад.

По сути оператор sleep заменяется на какую-то функцию, которая даже не производит никакой приостановки выполнения кода. С таким же успехом можно заменить sleep на функцию-пустышку.
Врема простоя и нагрузку на процессор проверял?

ziggi
13.01.2016, 20:05
Действительно, стоп. А теперь назад.

Врема простоя и нагрузку на процессор проверял?

Да, оно работает. Здесь же не подмена функции, а её "дополнение":


#define sleep%0\n%9 sleep FIXES_sleep(%0)


Вот во что превращается код после работы препроцессора:


main()
{
new a;
while (++a) {
printf("%d", a);
sleep FIXES_sleep ( (100)); }
}

$continue$
13.01.2016, 20:11
Один вопрос:


stock FIXES_sleep(ms)
{
// Call a native function that does very little, but saves the current
// heap pointer. Then return to save the accurate stack pointer.
return heapspace(), ms;
}

На какой сначала возвращаеться: heapspace а потом ms?
Если в итоге вернет ms?

ziggi
13.01.2016, 20:15
Один вопрос:


stock FIXES_sleep(ms)
{
// Call a native function that does very little, but saves the current
// heap pointer. Then return to save the accurate stack pointer.
return heapspace(), ms;
}

На какой сначала возвращаеться: heapspace а потом ms?
Если в итоге вернет ms?

heapspace не возвращается, а просто выполняется. Код написан "под" return для того, чтобы сократить размер AMX (по словам Y_Less).

Daniel_Cortez
13.01.2016, 20:54
Да, оно работает. Здесь же не подмена функции, а её "дополнение":


#define sleep%0\n%9 sleep FIXES_sleep(%0)


Вот во что превращается код после работы препроцессора:


main()
{
new a;
while (++a) {
printf("%d", a);
sleep FIXES_sleep ( (100)); }
}

Проверил. Хорошо, допустим, но за пределами main этот фикс всё так же не работает. Да и от вызова heapspace по прежнему нет никакого толку. Я попробовал убрать вызов той функции и фикс работал точно так же, никаких ошибок при выполнении в main. Замечу, я уже писал выше о бесполезности heapspace.

Вообще у Y_Less'а довольно интересный вариант. Никогда бы не подумал, что в Pawn можно использовать рекурсивные макросы, но Y_Less всё же придумал костыль (просто решением это назвать нельзя, поскольку его способ портит отступы, хоть это и видно только в препроцессе; да и способ этот далеко не самый простой). Но всё же надо будет запомнить тот метод.

ziggi
13.01.2016, 21:07
Проверил. Хорошо, допустим, но за пределами main этот фикс всё так же не работает.

Дак целью было избавиться от ошибки "stack/heap collision", а не заставить эту функцию работать везде.


Да и от вызова heapspace по-прежнему нет никакого толку. Я попробовал убрать вызов той функции и фикс работал точно так же, никаких ошибок при выполнении в main.

Хм, и правда. Видимо достаточно перед вызовом sleep просто вернуть какое-нибудь значение.