출처 : http://www.codegurus.be/codegurus/Programming/luahooks_en.htm

 

How to prevent LUA scripts that block your program

Introduction

In the previous two articles about LUA we learned how to embed LUA in C/C++.  But a nasty script can also block the C/C++ application by creating an endless loop.  An example of such a terrible script would be this :

If you execute such a script then your application will hang forever.  Luckily LUA provides hooks and Windows provides threads.  And for people who can't wait : the download is at the bottom of the page :-).

 

Threads

I'm not talking about LUA threads here, but about the threads of your operating system (Win32 in our case).  In order to make the script execution independent of your interface you have to create two threads.  One for the interface and one for the script execution.  If now a bad script will block the program it will only block the executioner thread and not the interface thread.

VERY IMPORTANT NOTE: the code below is only to show how to use a thread to avoid blocking of your program, it doesn't tell how to make LUA thread-safe/multithreaded!  Click on this link to read on how to make LUA multithreaded and avoid crashes.

To enable threads in your program you have to include process.h and you have to tell the compiler you're going to create a multithreaded program, go to the Project Settings, go to the C/C++ tab and select "Code Generation" as the category.

For the debug version select "Debug multithreaded" :

For the release version select "Multithreaded" :

In your program you can start a thread by calling the C library function _beginthread.  For example :

 

LUA Hooks/Events

Hooks/events are mainly used for debugging your LUA state but we can also use it to abort a running script.  We are interested in a hook/event called "The line hook".  This event will occur every time an instruction is about to be executed.  To set a hook we must call the lua_sethook function, it's defined as follows :

LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);

There are as you can see 4 parameters, the first one is (like always) the pointer to the LUA state, the second one is the address of a C function that handles the event.  The third parameter tells us for which kind of events the hook function will be called, the fourth parameter is only used for the LUA_HOOKCOUNT hook, see the manual.

We first create a global variable to tell us wether or not to abort the script.

Then we create the following short but very effective hook/event handler :

We will register the hook/event handler with lua_sethook as follows :

For more information about debugging and other types of hooks please refer to your LUA manual.

 

Modify your functions to protect against blocking scripts

Let's assume we create a script function sleep, the function will call the Windows Sleep function (this function suspends execution for X milliseconds).  We define this glue function as follows :

By implementing the function as such you have just given a bad LUA scripter the power to make your application hang for a long time.  The problem is this : if the scripter calls the sleep function with a high value, let's say : sleep(1500000) then your program will hang for 1500 seconds or 25 minutes!

A better implementation would be to sleep only for short moments and to check wether or not the script has been aborted, like this :

In the sleep function as above, the sleep is executed for a short duration of 100ms.  So if we want to suspend execution for 5 seconds (5000 milliseconds), then the Sleep function will be called 50 times, so every 50 times we check wether to abort the script/sleep function or not.

 

Event Objects

The sleep function defined above isn't that optimal, the program is executing a continuous loop eating CPU time.  Windows provides a better technique to avoid this: waitable event objects.  An event object allows threads to notify each other that some kind of an event has occured.  Windows has a function WaitForSingleObject it acts like Sleep but it will abort the function if the state of an event object is signaled (signaled means that the event has occured).

We create a global handle varible hCloseProgram to represent the event :

In the start-up of your program you create the event object using the CreateEvent function :

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttribute, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);

There are four parameters.  The first one represents the security attributes, we simple use NULL, the second one tells us we will control the event manually, the 3th parameter will be FALSE and that means that the initial state of the events is non-signaled, and the last parameter is a user-defined name (which can be NULL to create a nameless event) :

Our new glue function becomes this :

And in order to cancel the WaitForSingleObject, all you do is :

Of course, event objects still don't abort the LUA script executioner.  You also need to modify your hook function to teach it how to use the event object :

But the be honest, that's not a method I would recommend, the hook routine will be called a lot and calling every time the WaitForSingleObject function causes it to slow down and thus slowing down your scripts!  A better method would be a combination of both a global variable to allow aborting of the script engine and an event to allow aborting of functions that suspend the system.  See the LUA Demo 3 program to see how it is done.

I also invite you to experiment with another method : you can for example set only the LUA hook routine when you need to close your program.  For example :

 

About LUA Demo 3

This demo project is a bit more complicated than the other LUA downloadable examples.  It's an application that allows you to enter an LUA script that will be executed, either using a seperate thread or using only the main thread.  If no seperate thread is used the program will exit at the end of the script execution.  And in case of a seperate thread, the main thread will be suspended indefinitly (INFINITE) (until hCloseProgram becomes signaled).  This little test project is also safe against blocking scripts.  It includes most of the programming techniques explained above, feel free to browse and use the source code.  The program will also warn the user if the LUA script took too long to shut down (which normally shouldn't happen).

It's a nice LUA demonstration project, the following LUA functions are created :

clrscr( ) = clear the screen
getx( ) = get the current x coordinate
gety( ) = get the current y coordinate
gotoxy( ) = change the cursor position
printf( ) = print text
kbhit( ) = has a key been hit?
delay( ) = the sleep function (but in traditional conio.h style)
exit ( ) = exit the script

The program also allows you to save & load scripts, have you made a nice script with it?  Please e-mail it to me and show me what you've created with my litle demo program.  Thanks.

 

Download

You can download the above test project here (compressed ZIP files) :

The source code of LUA Demo 3
The executable version of LUA Demo 3

NOTE: All the required files are included in the source code file in order to compile succesfully

IMPORTANT: Even though the download contains multithreaded code, the LUA state isn't thread-safe in this example. Full LUA multithreading & thread-safety is discussed in this article (follow the link)!

 

Contact

If you have questions or remarks about this article or its contents, then feel free to contact me at <fibergeek @ codegurus.be>.  Don't forget to remove the white spaces or the e-mail won't arrive.

 

External links

LUA: the official site

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스

댓글을 달아 주세요