Page 1 of 2

Why does this snippet of code eat my CPU?

Posted: 01 Feb 2010, 21:35
by Caydr
The following bit of code causes my game to freeze for, strangely enough, exactly 0.25 of a second. (sarcasm)

Code: Select all

local clock = os.clock
~~~~
function sleep(n)
  local t0 = clock()
  while clock() - t0 <= n do end
end
~~~~
function widget:MousePress(mx,my,mb)
	if (mb == 2) then
		middledown = clock()
		repeat
			Spring.Echo('blabla')
			sleep(0.05)
		until ((clock() - middledown) > 0.25)
	end
end
I would find this at least sort of understandable if I used "while" instead of "if-with-a-repeat-and-pause", since it might do something silly like check to see if the condition was satisfied as fast as possible. Then again, CustomFormations uses "while" without any problems... maybe things concerning input are handled differently...?

I've got more than a few wild guesses why this might be happening, but I'm just frustrating myself and wasting time by endlessly rewriting the above code in different ways. For all I know it could simply be a mouse input bug, as was the culprit with some other things I've become familiar with recently.

Can someone help me understand what I'm doing wrong here?

Really enjoying this, by the way. I feel like I'm back in school, running into things that I can't do due to lack of knowledge rather than lack of time. I guess I could say it's refreshing... I'm not saying this to say "look at what a super-genius I am", but I really appreciate things that genuinely challenge my creativity rather than my attention span.

Re: Why does this snippet of code eat my CPU?

Posted: 01 Feb 2010, 21:39
by zwzsg
Well, because you coded it to wait exactly 0.25s?

I don't know why you'd want that, but I can tell you freezing Spring everytime someone press the mouse middle button won't make your widget popular.

Maybe you are confusing Lua and bos...

Re: Why does this snippet of code eat my CPU?

Posted: 01 Feb 2010, 21:45
by aegis
sleep in normal code blocks the thread, which will make everything freeze.
to wait for a period of time, check against a timer or clock in one of the Update() callins

Re: Why does this snippet of code eat my CPU?

Posted: 02 Feb 2010, 20:33
by Caydr
So why does using:
"while (bla - bla > 0.25)
do bla
if (bla - bla < 0.25)
break
end"

identically freeze my computer? Is it just checking every billionth of a millisecond to see if the conditions have been satisfied? This why I used pausing, I figured, "wait every 0.05" seconds and then re-check", would probably reduce the amount of unnecessary checking.
zwzsg wrote:Well, because you coded it to wait exactly 0.25s?
OH COME ON I EVEN SAID "SARCASM", I CAN SEE THE 0.25 IN THE CODE I WROTE. COME ON. REALLY, ZWZSG. REALLY.

BTW for a bit of fun, remove the pause and the until and watch your SSD's lifespan get cut in half while you try to find a way to close Spring. Maybe the disk writes should be cached in RAM and only written out every half second or so?

Re: Why does this snippet of code eat my CPU?

Posted: 02 Feb 2010, 21:52
by Tobi
That'd make debugging impossible. (crash -> no chance anymore to commit the last (most relevant) lines to the disk)

Re: Why does this snippet of code eat my CPU?

Posted: 02 Feb 2010, 21:56
by aegis
aegis wrote:to wait for a period of time, check against a timer or clock in one of the Update() callins

Re: Why does this snippet of code eat my CPU?

Posted: 02 Feb 2010, 21:58
by SeanHeron
Ok - my understanding is still a bit shallow, but basicly its so:
Unlike bos scripts (of which I know little - but that at least in effect run "parallel" to everything else), gadgets synced code (and from what you're saying I assume also their unsynced code, and widgets) get processed one after the other.
Ie for a certain callin (GameFrame or MousePress) the engine takes all the gagdets (and widgets) there are, and walks through them, one after the other (according to their layer order). It won't go on to the next until it's finished with the one it's processing - and the whole rest of the simulation get's to wait as well .
Cause otherwise, at least for synced code, you'd have real trouble with calculations in gadgets being done faster relative to the rest of the simulation on some players computers then on the next (oh - player 1's com got a HP boost from gadget before he get's struck by missile - player 2's doesn't and dies... :P).

Going with that knowledge, it should now be obvious that all the ways you were trying to solve it (ie do your stuff for .25 seconds - directly after/within the callin!) can only lead to the fail behaviour you're getting :P.

As aegis correctly pointed out, what you want to do is set a variable (and a timer value), and then check for the variable from widget:Update() [well - as well as the timer, and reset that after you're required period].
If you required greater precision you could also use widget:GameFrame(f), but that shouldn't usually be necessary.

Excuse my arguing from a Gadget perspective, it's where I know my way round best (but like I say, it seems the same applies to unsynced code / widgets)

Re: Why does this snippet of code eat my CPU?

Posted: 03 Feb 2010, 20:44
by Caydr
Tobi wrote:That'd make debugging impossible. (crash -> no chance anymore to commit the last (most relevant) lines to the disk)
I can appreciate where this is true - but we're rapidly moving towards widespread SSD adoption. Spring is the only program I know of that writes in such a potentially dangerous way with no safeguards of any kind. IIRC, Spring wrote 1024 times to my HDD in the space of a quarter second when I was first working on this script, but I hadn't yet added the "end when..." part to it. That's 250,000 writes a minute, and low-cost SSDs (which most people are buying) have a projected lifespan per sector of only tens of thousands. This means that a single lua script could really harm someone's computer if things went awry.

Just saying "well, don't use lua you don't trust" or "that's a stupid mistake" isn't good enough. Regular people will (reasonably) think that a game engine isn't going to damage their computer's hardware. Yes it's a stupid mistake, but if there were't stupid mistakes made all the time, we wouldn't have a help and support forum.

In a world of rickrolls and mudkips, we could really do without a wide-open invitation like this.

Perhaps run a separate process which handles writing to infolog and which won't be affected by a crash. Then just limit writes to every second or so.

Re: Why does this snippet of code eat my CPU?

Posted: 03 Feb 2010, 20:56
by aegis
if your ssd has wear leveling (which it probably will), you need to write the maximum number of cycles to most of the disk for it to be a problem. due to the drive's internal buffer, writing to the ssd 100k times might not use anywhere near 100k write cycles.

Re: Why does this snippet of code eat my CPU?

Posted: 03 Feb 2010, 22:16
by zwzsg
Caydr wrote:Perhaps run a separate process which handles writing to infolog and which won't be affected by a crash. Then just limit writes to every second or so.
Hey, no, I don't wan't to risk losing debug info when Spring crash unexpectedly!

Why isn't the OS caching file writes anyway?

Re: Why does this snippet of code eat my CPU?

Posted: 03 Feb 2010, 22:28
by Argh
Just use GameFrame(), Caydr. I.E.:

Code: Select all

function widget:MousePress(mx,my,mb)
   if (mb == 2) then
         blahblah =somenumber, a table entry, whatever
   end
end

function widget:GameFrame(f)
if f % 8 < 0.1 then -- approximately every quarter-second, if people's CPUs aren't chugging
  if blahblah == somenumber, is a table with an entry at [1], etc.
then
-- do whatever you want here
-- reset blahblah back to nil, zero, whatever
end
end
end
For most things, this is really safe, and won't cause evil CPU-eating errors. As for the rest of this stuff... meh, this is sorta-real programming, Caydr. You can do various things that can work your hardware really hard, if you screw up badly enough. Just how it is- if you're worried about your SSD, use a SATA drive for paging RAM while working on Spring code, or just turn off paging entirely.

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 02:43
by Ralith
On linux/ext3+, at least, filesystem writes *are* cached--by the OS. I imagine Windows does something similar. It's not Spring's job to worry about that sort of thing.

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 05:09
by SpliFF
exactly, it's called write-caching and it isn't the programs' job. Look it up, enable it if you want it (you generally need a UPS to make write-caching a sensible option).

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 05:17
by lurker
Anyway, Caydr, the problem is that you're not reading the code literally enough. When it says while(x) wait() end, it means WHILE. It stays in that loop and does nothing else whatsoever until it escapes. If it was going to change the game behind your back there would have to be a lot of complicated interlock code to do anything in lua.

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 06:55
by Niobium
Argh wrote:Just use GameFrame(), Caydr. I.E.:
Except that GameFrame is affected by the game speed, pausing, etc. Update(dt) is much more appropriate (where dt is roughly the time since the last Update call)

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 10:23
by Caydr
Yeah that was my concern, I'd read elsewhere that conventional methods of "waiting" were affected by the game's speed. For this function, as it is part of mouse input, I need 0.25 seconds to really be 0.25 seconds.

I imagine I just don't know the proper way of doing what I'm trying to do. Actually I didn't even know there was a "while" command until a few days ago, so...

Can someone suggest a better way of handling this?

Code: Select all

function widget:MousePress(mx,my,mb)
   if (mb == 2) then
      middledown = clock()
      repeat
         Spring.Echo('blabla')
         sleep(0.05)
      until ((clock() - middledown) > 0.25)
   end
end
Basically what I need to do is just be sure that the mouse button has been held down for 0.25 consecutive real seconds before the script activates, else it resets and waits for the next time the mouse button goes down.

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 10:36
by Niobium
MousePress -> remember current time
MouseRelease -> check new current time vs remembered time, if >0.25 sec -> fire

Something like that would be the best, no need for loops/sleeps that way. You can use os.clock() which gives you the seconds since lua started (so is nice for comparing differences in time), or use the slightly more complicated spring timer functions, which I think are more accurate.

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 10:38
by SeanHeron
You're welcome Caydr, I appreciate it.

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 10:49
by Regret

Code: Select all

local midclicktimer --placeholder
local pm1,pm2,pm3 --placeholder
function widget:Update()
	local _,_,m1,m2,m3 = Spring.GetMouseState() --get mouse button status
	if (m2 and (not pm2)) then --is pressed and was not pressed before == was pressed right now
		midclicktimer = os.clock() + 0.25 --initialize timer
	end
	if (midclicktimer ~= nil) then --timer initialized?
		if (m2) then --is button still pressed?
			if (os.clock() >= midclicktimer) then --enough time passed?
				midclicktimer = nil --reset timer
				yourfunction() --do shit
			end
		else --nope
			midclicktimer = nil --reset timer
		end
	end
	pm1,pm2,pm3 = m1,m2,m3 --remember last mouse state
end

Re: Why does this snippet of code eat my CPU?

Posted: 04 Feb 2010, 17:16
by aegis
polling is ugly <_<