Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
OmarShehata
Party member
Posts: 259
Joined: Tue May 29, 2012 6:46 pm
Location: Egypt
Contact:

I'm having some trouble understanding how threads/channels work in 0.9.0

So I get that there's the Channel API to communicate with threads, but I don't get how to "link" a channel with a thread.

I create a new thread with http://www.love2d.org/wiki/love.thread.newThread, and a new channel with http://www.love2d.org/wiki/love.thread.newChannel or getChannel...and then what? How do I tell it that Channel belongs to that thread..? Any examples on how to use it?

bartbes
Sex machine
Posts: 4871
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

There is no direct relationship between channels and threads. Channels are basically thread-safe, shared queues. There are two kinds of channels, with subtly different mechanics.

Named channels: These keep their contents even without references, and are available globally, using getChannel. They also get created when they don't exist yet, so just calling getChannel with a non-existent name gives you a fresh one. (Technically, they are also destructed when they are empty, and there are no references, but that's not important.)

Unnamed channels: These get collected normally, and you can only obtain them from newChannel once. If you want to use it in multiple places, you need to find a way to get them there, like the new parameters to [wiki]Thread:start[/wiki], or sending them over another channel.

Of course this doesn't mean you can't pass named channels around, it's just that there's a central "registry" for them.

Code: Select all

-- main
t:start(c1)
c2:supply(c1:demand())

c1 = ...
c1:supply("Hello, world!")
c2:push(c1:demand())

Cowardly acts and the eating of birds must not be the deeds of a Hero of Storms.

OmarShehata
Party member
Posts: 259
Joined: Tue May 29, 2012 6:46 pm
Location: Egypt
Contact:

Thanks for the quick reply bartbes! I just got it to work.

Germanunkol
Party member
Posts: 704
Joined: Fri Jun 22, 2012 4:54 pm
Contact:

bartbes wrote:

Code: Select all

-- main
t:start(c1)
c2:supply(c1:demand())

c1 = ...
c1:supply("Hello, world!")
c2:push(c1:demand())

Can you name some advantages? Why was the API changed so much?
trAInsported - Write AI to control your trains
Bandana (Dev blog) - Platformer featuring an awesome little ninja by Micha and me
GridCars - Our jam entry for LD31
Germanunkol.de

bartbes
Sex machine
Posts: 4871
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

That's a convoluted example, it's not harder, and in fact has way less edge cases and gotchas.
Cowardly acts and the eating of birds must not be the deeds of a Hero of Storms.

Helvecta
Party member
Posts: 166
Joined: Wed Sep 26, 2012 6:35 pm

Allow me to revive this thread to throw into the hat the not-so-convoluted example of how to use love.thread:

Code: Select all

-- main.lua
i = {}
end
function love.update(dt)
v = channel:pop()
if v then
table.insert(i, v)
end
end
function love.draw()
love.graphics.print(tostring(i[1]), 10, 10)
end

c:push("hi")
Does it look pretty? Eh, maybe not.

Does it work? You bet your muffins.
"Bump." -CMFIend420

s-ol
Party member
Posts: 1070
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Is there a preferred way to implement locks? I think blocking with :demand() on a named channel should do the trick but maybe there's something better I'm not aware of.

Code: Select all

-- create Lock
lock:push(-1) -- message = free

-- obtain lock
lock:demand() -- pop the free message or wait for it

-- do something

-- release lock
lock:push(-1)
It would be nice to save a thread-specific value in the lock that identifies the thread that obtained the lock though.

Edit: more questions:

2. :demand() is implemented in "smart" C++ (vs. long polling), right?
3. Can I somehow build an atomic "tryDemand" method?
If I want to synchronize two threads that work on one array-table of objects, I would write something like this:

Code: Select all

for _,v in ipairs( sharedtable ) do
if sharedtable.lock:getCount() > 0 then
sharedtable.lock:demand() -- obtain
-- do stuff
sharedtable.lock:push(-1) -- release
end
end
However this is not thread safe, both threads could check getCount() at the same time, then one of them obtains the lock whilst the other thread is blocked for the whole update, after which it proceeds to update the same object a second time. The double-updating can be prevented with a simple flag, but this still wastes quite some time, especially if the list is long and this happens quite often.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
baby:hurt(me)
end

s-ol
Party member
Posts: 1070
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

And here we go, found a possible answer by myself (3.):
Non-blocking locks can be done like this:

Code: Select all

-- in the "scheduler" thread7
update = not (update or true)

for _,v in ipairs(sharedtable) do
if v.lock:pop() ~= update then    -- if the lock has been obtained this returns nil, if it has already been updated it returns update
-- do something
end
l.lock:push( update ) -- flag object as done
end

Aaaaand the first actual problem: If I may only supply flat tables (via Thread.run and channels), how do I make threads cooperatively work on a list of entities?

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
baby:hurt(me)
end

Azhukar
Party member
Posts: 478
Joined: Fri Oct 26, 2012 11:54 am

S0lll0s wrote:how do I make threads cooperatively work on a list of entities?
I believe the limit for number of channels is pretty high.

Would be pretty hilarious to send a flat table full of channels, each channel representing a single table index.

s-ol
Party member
Posts: 1070
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Azhukar wrote:
S0lll0s wrote:how do I make threads cooperatively work on a list of entities?
I believe the limit for number of channels is pretty high.

Would be pretty hilarious to send a flat table full of channels, each channel representing a single table index.
that could actually.... work xD

Code: Select all

local meta = love.thread.getChannel("meta")
local keys = {}
for k,v in pairs( ent ) do
keys[#keys+1] = k
end
meta:supply( keys )
for _,k in ipairs( keys ) do
end

Having a way to share actual tables would still be much better. And I believe every OS has a method to share RAM between processes (or at least threads)

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
baby:hurt(me)
end

### Who is online

Users browsing this forum: Google [Bot] and 14 guests