Thread example (DEMO)

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

Thread example (DEMO)

Post by dreadkillz »

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 387 times
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Thread example (DEMO)

Post by Ref »

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?
User avatar
dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

Re: Thread example (DEMO)

Post by dreadkillz »

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.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Thread example (DEMO)

Post by Ref »

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!
User avatar
dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

Re: Thread example (DEMO)

Post by dreadkillz »

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.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Thread example (DEMO)

Post by Ref »

Really help me! Thanks!
Just playing around with your script.
Attachments
threadDemo4.love
ThreadDemo patched but has problem
(14.55 KiB) Downloaded 253 times
Last edited by Ref on Thu Jul 12, 2012 4:13 pm, edited 2 times in total.
User avatar
dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

Re: Thread example (DEMO)

Post by dreadkillz »

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.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Thread example (DEMO)

Post by Ref »

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 234 times
Last edited by Ref on Thu Jul 12, 2012 10:31 pm, edited 1 time in total.
User avatar
dreadkillz
Party member
Posts: 223
Joined: Sun Mar 04, 2012 2:04 pm
Location: USA

Re: Thread example (DEMO)

Post by dreadkillz »

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
error.png (18.35 KiB) Viewed 7824 times
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Thread example (DEMO)

Post by Ref »

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 288 times
Post Reply

Who is online

Users browsing this forum: No registered users and 139 guests