Following on from my recent thread "Speed of Texture Loading" I thought I'd try and experiment with a plugin that can execute blocking/time consuming Lua functions on a background thread. The eventual aim (in my case) would be to call TexturePack.new() on a background thread so it doesn't interrupt any existing animation that is currently being played.
Now I've taken a look at Lua Lanes, which seems to be the general solution to the problem, but frankly getting this working would be beyond me at this stage in my overall Lua experience. Also, I'd like to test whether the TexturePack functionality could successfully be executed on a different thread before spending too much time on a general solution.
This is what I have come up with so far. It seems to me that if I can get this working it might be a useful plugin for when a full Lanes approach is not required. Lua test code is:
local overlapper=require("overlapper")
function wait(ms)
local start = os.timer()
local finish = start+ms
while (finish > os.timer()) do
end
end
-- First try function containing simple assignment expression to local
local answer
overlapper.overlapCall(function() wait(3) answer=3+4 end)
-- Print answer. First print should be nil since funtion delays for 3s
-- Second print should be corect since waits for completion
print("answer=", answer)
overlapper.overlapWait()
print("answer(wait)=", answer) |
//
// overlapper.mm
// GiderosiPadPlayer plugin
//
#include "gideros.h"
#include "lua.h"
#include "lauxlib.h"
#import <Foundation/Foundation.h>
<a href="https://forum.giderosmobile.com/profile/interface" rel="nofollow">@interface</a> OverlapThread : NSObject
+(void)start: (id) Lid;
<a href="https://forum.giderosmobile.com/profile/end" rel="nofollow">@end</a>
<a href="https://forum.giderosmobile.com/profile/implementation" rel="nofollow">@implementation</a> OverlapThread
NSLock* lock=NULL;
+(void)start: (id) Lid
{
lua_State* L= (lua_State*)[Lid pointerValue];
if (lock==NULL)
{
lock=[[NSLock alloc] init];
}
[lock lock];
assert(lua_isfunction(L, -1)==1);
if (lua_isnil(L, -1)==0)
{
usleep(1000);
lua_call(L, 0, 0);
}
[lock unlock];
}
<a href="https://forum.giderosmobile.com/profile/end" rel="nofollow">@end</a>
static int overlapCall(lua_State *L)
{
assert(lua_isfunction(L, -1)==1);
lua_State *threadL=lua_newthread(L);
lua_xmove(L, threadL, 2);
lua_xmove(threadL, L, 1);
assert(lua_isfunction(threadL, -1)==1);
assert(lua_isthread(L, -1)==1);
[NSThread detachNewThreadSelector:<a href="https://forum.giderosmobile.com/profile/selector%28start" rel="nofollow">@selector(start</a><img class="emoji" src="https://forum.giderosmobile.com/resources/emoji/smile.png" title=":)" alt=":)" height="20" /> toTarget:[OverlapThread class] withObject: [NSValue valueWithPointer: threadL]];
return 1;
}
static int overlapWait(lua_State *L)
{
[lock lock];
[lock unlock];
return 0;
}
static int loader(lua_State *L)
{
//This is a list of functions that can be called from Lua
const luaL_Reg functionlist[] = {
{"overlapCall", overlapCall },
{"overlapWait", overlapWait },
{NULL, NULL},
};
luaL_register(L, "overlapper", functionlist);
//return the pointer to the plugin
return 1;
}
static void g_initializePlugin(lua_State* L)
{
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
lua_pushcfunction(L, loader);
lua_setfield(L, -2, "overlapper");
lua_pop(L, 2);
}
static void g_deinitializePlugin(lua_State *L)
{ }
REGISTER_PLUGIN("overlapper", "1.0") |
So the above seems to work. However, I have two issues on which I'm asking for comments:
1) If I change the test code above to perform a TexturePack.new() rather than a simple addition then I get an EXC_BAD_ACCESS somewhere deep inside TexturePackBinder.create. Now this could either be an issue with TexturePack itself (in which case a comment from
@atilim would be helpful) or just as likely a problem with my approach. Potentially the issue could be:
2) I've followed, in part, the approach in
http://lua-users.org/wiki/ThreadsTutorial and in there the author mentions a way of protecting the Lua global environment by locks defined in a custom luauser.h. I haven't managed to find whether it is possible in Gideros to override these lock functions in this way. Any suggestions on whether this is possible and how to do it, would be most gratefully received.
Best regards
Comments
I think, it's very normal that TexturePack.new is crashing on a different thread because it's not a thread-safe class/function. Currently, there is no way to load a texture on a different thread.