I want to forcibly stop the thread created by dispatch_async
if its in use for too much time, for example pass over 5 minutes. By searching over the internet, I got some one thought there was no way to stop the thread, does any one know that?
In my imagine, I want to create a NSTimer
to stop the thread when time specified passed.
+ (void)stopThread:(NSTimer*)timer
{
forcibly stop the thread???
}
+ (void)runScript:(NSString *)scriptFilePath
{
[NSTimer scheduledTimerWithTimeInterval:5*60 target:self selector:@selector(stopThread:) userInfo:nil repeats:NO];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[LuaBridge runLuaFile:scriptFilePath];
});
}
My runLuaScript method:
+ (void)runLuaFile:(NSString *)filePath
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
int error2 = luaL_dofile(L, [filePath fileSystemRepresentation]);
if (error2) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_close(L);
}
Dear @Martin R, should I use lstop like that, and when I want to stop the thread, just call stopLuaRunning
method?
static lua_State *L = NULL;
+ (void)runLuaFile:(NSString *)filePath
{
L = luaL_newstate();
luaL_openlibs(L);
int error2 = luaL_dofile(L, [filePath fileSystemRepresentation]);
if (error2) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_close(L);
}
+ (void)stopLuaRunning:(lua_State *L)
{
lua_sethook(L, NULL, 0, 0);
luaL_error(L, "interrupted!");
}
You cannot kill a running block. You have to implement runLuaFile
in a way that it works asynchronously and can be cancelled.
For example if running the script is done via NSTask
, you can use terminate
to kill
the task if it is running too long.
NSOperation
will probably not help because cancel
relies on the operation to be
"cooperative": the operation has to check regularly if it has been cancelled. That will not
stop the running runLuaFile
method.
UPDATE: From inspecting the source code "lua.c" of the Lua interpreter, it seems to
me that you can cancel a running script using lua_sethook
.
A very simple implementation (using a static variable for the Lua state) would be:
static lua_State *L = NULL;
+ (void)runLuaFile:(NSString *)filePath
{
L = luaL_newstate();
luaL_openlibs(L);
int error2 = luaL_dofile(L, [filePath fileSystemRepresentation]);
if (error2) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_close(L);
L = NULL;
}
static void lstop (lua_State *L, lua_Debug *ar)
{
lua_sethook(L, NULL, 0, 0);
luaL_error(L, "interrupted!");
}
+ (void)stopLuaRunning
{
if (L != NULL)
lua_sethook(L, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
A more elegant solution would use store the Lua state in an instance variable of the class
and make runLuaFile
and stopLuaRunning
instance methods instead of class methods.
You should use NSOperation
and NSOperationQueue
as they have built in support for cancellation so your operation can check if it's cancelled and your timer just calls cancel
on the operation.
By searching over the internet, I got some one though there was no way to stop the thread, does any one know that?
Don't bother; It's not yours to stop. If you have a reference to a queue, then you can call dispatch_release
and it will be destroyed at the appropriate time, but you would not do this with the global queue.
Killing that thread would just kill a thread in the queue's pool and should be considered as good as undefined behavior.
If you want to control a thread's lifetime, create your own thread and interact with its run loop. But do ensure your programs return from their implementations normally -- don't just kill stuff because it isn't working for you or never returning. Martin R mentioned how this would happen -- your task should support timeout, cancellation, or another means to stop itself in the event the task has gone rogue.
Wain's also mentioned a good middle ground.
Use NSOperation and NSOperationQueue.
Here is a long, but helpful guide.
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues
The key point for you in this case, is there example of overriding main.
@interface MyLengthyOperation: NSOperation
@end
@implementation MyLengthyOperation
- (void)main {
// a lengthy operation
@autoreleasepool {
for (int i = 0 ; i < 10000 ; i++) {
// is this operation cancelled?
if (self.isCancelled)
break;
NSLog(@"%f", sqrt(i));
}
}
}
@end
Notice the if (self.isCancelled) in the middle of the loop.
This is the "modern" iOS way of managing background operations without creating your own threads and managing them directly.