Browse Source

Added Channel:performAtomic(func, ...). Resolves issue #1034.

performAtomic calls the function and passes the Channel as the first argument, plus any additional specified arguments. The called function should do as little work as possible.

To other threads, all methods for that Channel are executed in a single step inside the function. This can prevent a large number of potiential race conditions.

Example:

-- main.lua:
local function set(channel, value)
    channel:clear()
    channel:push(value)
    return "set value"
end

assert(channel:performAtomic(set, 1) == "set value")

-- thread.lua
while true do
    assert(channel:peek() == 1)
end
Alex Szpakowski 10 years ago
parent
commit
289eeb4fe0

+ 1 - 0
src/modules/thread/Channel.h

@@ -39,6 +39,7 @@ class Channel : public love::Object
 // FOR WRAPPER USE ONLY
 friend void retainVariant(Channel *, Variant *);
 friend void releaseVariant(Channel *, Variant *);
+friend int w_Channel_performAtomic(lua_State *);
 
 public:
 

+ 32 - 2
src/modules/thread/wrap_Channel.cpp

@@ -24,6 +24,7 @@ namespace love
 {
 namespace thread
 {
+
 void retainVariant(Channel *c, Variant *v)
 {
 	c->lockMutex();
@@ -116,6 +117,33 @@ int w_Channel_clear(lua_State *L)
 	return 0;
 }
 
+int w_Channel_performAtomic(lua_State *L)
+{
+	Channel *c = luax_checkchannel(L, 1);
+	luaL_checktype(L, 2, LUA_TFUNCTION);
+
+	// Pass this channel as an argument to the function.
+	lua_pushvalue(L, 1);
+	lua_insert(L, 3);
+
+	c->lockMutex();
+
+	// call the function, passing the channel as the first argument and any
+	// user-specified arguments after.
+	int numargs = lua_gettop(L) - 2;
+	int err = lua_pcall(L, numargs, LUA_MULTRET, 0);
+
+	c->unlockMutex();
+
+	// Unfortunately, this eats the stack trace, too bad.
+	if (err != 0)
+		return lua_error(L);
+
+	// The function and everything after it in the stack are eaten by the pcall,
+	// leaving only the Channel argument. Everything else is a return value.
+	return lua_gettop(L) - 1;
+}
+
 static const luaL_Reg type_functions[] = {
 	{ "push", w_Channel_push },
 	{ "supply", w_Channel_supply },
@@ -124,6 +152,7 @@ static const luaL_Reg type_functions[] = {
 	{ "peek", w_Channel_peek },
 	{ "getCount", w_Channel_getCount },
 	{ "clear", w_Channel_clear },
+	{ "performAtomic", w_Channel_performAtomic },
 	{ 0, 0 }
 };
 
@@ -131,5 +160,6 @@ extern "C" int luaopen_channel(lua_State *L)
 {
 	return luax_register_type(L, THREAD_CHANNEL_ID, type_functions);
 }
-}
-}
+
+} // thread
+} // love

+ 1 - 0
src/modules/thread/wrap_Channel.h

@@ -36,6 +36,7 @@ int w_Channel_demand(lua_State *L);
 int w_Channel_peek(lua_State *L);
 int w_Channel_getCount(lua_State *L);
 int w_Channel_clear(lua_State *L);
+int w_Channel_performAtomic(lua_State *L);
 
 extern "C" int luaopen_channel(lua_State *L);
 } // thread