## Thread example (DEMO)

Showcase your libraries, tools and other projects that help your fellow love users.
dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

### Thread example (DEMO)

I wrote a little demo demonstrating how to use threads and to teach myself. As you can see from the demo's code, it's pretty simple to use.

main.lua

Code: Select all

-- new thread
thread = love.thread.newThread('thread','thread.lua')
thread:start()

function love.update(dt)
msg_table		= msg_table or {}
-- process messages when other thread is "done"
if thread:get('done') then
msg_table[1]	= thread:get('number')
msg_table[2]	= thread:get('string')
msg_table[3]	= tostring(thread:get('condition'))
-- concat our messages into a long string
message			= table.concat(msg_table,'\n')
-- get image data from the other thread
imageData		= imageData or thread:get('image')

-- initialize image
image	= love.graphics.newImage(imageData)
end
if thread:get('hasCounter') then
msg_table[4]	= thread:get('counter')
message			= table.concat(msg_table,'\n')
end
end

function love.draw()
if image then
love.graphics.draw(image,50,50)
end
if message then
love.graphics.print(message,10,10)
end
end

thread.lua

Code: Select all

-- load modules b/c new thread
require 'love.filesystem'
require 'love.image'
require 'love.timer'

-- get this thread's id
thisThread	= love.thread.getThread()
-- load image file, userdata can be sent also!
file		= love.image.newImageData('love.png')

-- send messages to this thread's inbox
thisThread:set('image',file)
thisThread:set('number',1337)
thisThread:set('string','a string message')
thisThread:set('condition',true)
thisThread:set('done',true)

time0 = love.timer.getTime()
counter = 0

-- count per second and send counter to inbox
while true do
timeD = love.timer.getTime() - time0
if timeD > 1 then
counter = counter + 1
thisThread:set('counter',counter)
thisThread:set('hasCounter',true)
time0 = love.timer.getTime()
end
end

Attachments
threadDemo.love
+0.8.0
(13.76 KiB) Downloaded 232 times

Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

### Re: Thread example (DEMO)

Wondering if my observation is correct.
It seems that 'get' clears 'set'????????????
Example
Using your code as is:

Code: Select all

msg_table[3]	= "Condition: "..tostring(thread:get('condition'))

I get msg_table[3] is 'true'.
If I add another 'get' immediately after the above line

Code: Select all

msg_table[3]	= "Condition: "..tostring(thread:get('condition'))
msg_table[3]	= "Condition: "..tostring(thread:get('condition'))

I get msg_table[3] is 'nil'.
What am I doing wrong?

dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

### Re: Thread example (DEMO)

Get clears your message Inbox that you Set. If you want to leave your message in your inbox, you use Peek. I forgot to note that.

Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

### Re: Thread example (DEMO)

Thanks dreadkillz!
Works!
So you 'peek' status messages but 'get' userdata? (Tried peeking an image - didn't work.)

Your demo has some strange features:
1. Current thinking seems to indicate that it is preferable to use 'love.load' rather than just dumping values at the start of the script.
2. The 'main' does nothing while 'thread' is loading the image. No real reason to use a thread. Would be nice to have something being displayed while image is loading in the background.
3. I thought that concating strings each frame was not such a good idea. You might want to try something like the following for displaying text from a table.

Code: Select all

function showData()
locx, locy = 10,10
for i = 1, #msg_table do
love.graphics.print( msg_table[i], locx, locy + i * 12 )
end
end

Just a suggestion.
4. The script in the thread never ends.
5. The main script lacks a way to terminate (like escape key).
Other than that, thanks for the demo.
Learned a lot!

dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

### Re: Thread example (DEMO)

Thanks for the feedback! Yes it's not the best demo and there's a lot of different ways of doing things. It was really made to demonstrate how threading works and as learning experience for me. As long as it helps somebody out there, that's good enough for me.

Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

### Re: Thread example (DEMO)

Really help me! Thanks!
Just playing around with your script.
Attachments
threadDemo4.love
ThreadDemo patched but has problem
(14.55 KiB) Downloaded 100 times
Last edited by Ref on Thu Jul 12, 2012 4:13 pm, edited 2 times in total.

dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

### Re: Thread example (DEMO)

Your demo crashes for me. I checked the code and what probably happens is that the main thread peeks for the 'image_loaded' message at love.draw before love.update gets to create the image object. You can't be sure that the main thread is at the correct code block when you send that message since threads run concurrently! You wanna change that if thread:peek( 'image_loaded' ) to if image in love.draw.

Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

### Re: Thread example (DEMO)

Very strange as it runs (as it shouldn't) on my computer.
Have added 'updateStatus2()' to love.load() which should (I hope) fix this error and updated the previous posting.
Thanks for taking the time to check my code.
Still learning and appreciate any and all comments.
Best
EDIT: The above change shouldn't have any effect (really doesn't do anything).
Added 'thisThread:set('image_loaded',false)' to top of thread.lua:

Code: Select all

thisThread	= love.thread.getThread()
thisThread:set( 'start', true )
thisThread:set('image_loaded',false)

Which should solve the problem but now I randomly get the error you report (approximately 1 out every 10 times I run the code).
So there is something fundimentally wrong in the code.
Makes me very concerned about using threads until I can figure out what I'm doing wrong.
Edit: May be related to Love not liking to have image repeatedly uploaded - manybe a timing problem?
Tried forcing garbagecollection after each load to no avail. Still have random error (not having image data available).
Could revert back to checking if image data available but that circumvents the test of image <> thread communication.
Edit2: Ok, tried using a test for image data rather than image_loaded being true. Appreciate it if you can reliablely run this (attached) version. Still don't appreciate where the problem lies.
Edit3: Updated demo to a more robust version with fewer blockhead errors (I hope).
Attachments
threadDemo5.love
Cleaned up version of thread demo (I hope)
(14.67 KiB) Downloaded 92 times
Last edited by Ref on Thu Jul 12, 2012 10:31 pm, edited 1 time in total.

dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

### Re: Thread example (DEMO)

Your 2nd version works perfectly for me. It's a little tricky because you have to synchronize your threads. I'll try to give a deeper explanation of your original demo.

Recall the game loop: love.run:
while
...
love.update()
love.draw()
...
end

Here's what happens:

The 2nd thread sets the 'image_loaded' message.
When the 'image_loaded' message is sent to the inbox, the main thread is at an unknown location in its loop.

The error occurs when the main thread is currently between "start" and "end" (see below):

Code: Select all

function love.update(dt)
if thread:peek('image_loaded')  then
imageData		= imageData or thread:get('image')
image			= love.graphics.newImage(imageData)
cx, cy, rd		= 200, 200, 125 -- center of clock
updateStatus2()
end

-------------------------
-- start******
-------------------------

if thread:peek( 'hasCounter' ) then
msg[3]	= "Counter: "..tostring(thread:peek('counter'))
end
end

function updateStatus2()
msg[1]	= "Image loaded: "..tostring(thread:peek('image_loaded'))
msg[2]	= "Counter active: "..tostring(thread:peek("hasCounter"))
if thread:peek('hasCounter',test) then
msg[3]	= "Counter: "..tostring(thread:peek('counter'))
end
msg[4]	="fab_ready: "..tostring(thread:peek('fab_ready'))
if thread:peek("fab_ready") then
msg[5]	= "Fibonacci #37: "..tostring(thread:peek('fab_val'))
end
if not thread:peek('thread_running') then
msg[6] = "Demo complete!"
end
end

function love.draw()
drawClock()
gr.print( math.floor( lt.getTime()), cx, cy+rd+2*fontsize )

-------------------------
-- end******
-------------------------

if thread:peek( 'image_loaded' ) then
love.graphics.draw( image, hx-.2*rd, hy-.2*rd, 0, .2, .2 )
showData()
else
elapsedTime()
gr.print("Image loading in thread: "..counter,100,fontsize)
end
end

After "end", the main thread then peeks for the 'image_loaded' message in love.draw and returns true for the control structure (if). It then executes the code block in the control structure and tries to draw the non-existent image object. If the main thread was running a line before "start" or after "end" when the message is set, the error would not occur. This is why it seems so random! Hope that helps.

Error message for the original version attached below.
Attachments
error.png (18.35 KiB) Viewed 3519 times

Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

### Re: Thread example (DEMO)

Right!
Just a blockhead here.
Should learn to read more carefully look at the code.
Thanks for your help.
Gives me a lot more confidence in using threads.
Edit: OK! Latest and Greatest!
Demo now has:
- delayed thread start
- used getKeys
- 2 way communication main <> thread
- thread termination ( I think - hope)
Attachments
threadDemo6.love
Just a demo
(14.97 KiB) Downloaded 144 times

### Who is online

Users browsing this forum: No registered users and 6 guests