B+: Simple time-reminder without 100% CPU usage?

Blitz3D Forums/Blitz3D Beginners Area/B+: Simple time-reminder without 100% CPU usage?

BachFire(Posted 2003) [#1]
Hey guys. (problem stated after code bit..)

I'm trying to do a simple program, where you can enter a given number of minutes in a field. It will then remind you when that time is up. If you enter 1, it will remind you after 1 minute. Code:

MainWindow=CreateWindow ("Reminder",GraphicsWidth()/2-100,GraphicsHeight()/2-75,200,150,0,5)
Global MinField=CreateTextField (10,25,160,20,MainWindow)
Global GoButton=CreateButton ("Go !",10,55,60,20,MainWindow)

Global counter=0
Global target=0
Global MilTemp=0


Repeat
 eid=WaitEvent()
 Select eid
 Case $803
 Exit
 End Select

 If EventSource()=GoButton
  If EventID()=$401
   Go()
  End If
 End If
Forever


Function Go()
target=Int(TextFieldText(MinField))*60
MilTemp=MilliSecs()

Repeat
 If MilTemp<=(MilliSecs()-1000) Then
  MilTemp=MilliSecs()
  If counter=0 Then
   counter=1
  Else
   counter=counter+1
  End If
  If counter=target Then Notify "Time is up!" : Exit
 End If
Until KeyHit(1)
End Function


I'll facelift the program, when the code is okay. Here's my problem. While the time is running, I get 100% CPU usage. Isn't that a bit much? Is there some other way to do this, without that much CPU load?


BachFire(Posted 2003) [#2]
Hey again. - I'm also a little worried, that it might not be 100% precise. Any way of ensuring precision?


ashmantle(Posted 2003) [#3]
you could do a check for
millisecs() > timestarted + timefinished

instead of doing a counter.

when you set the alarm you make TimeStarted = Millisecs()


should be fairly accurate..


ashmantle(Posted 2003) [#4]
instead of target=Int(TextFieldText(MinField))*60

you should make it target=Int(TextFieldText(MinField))*3600

because Millisecs() returns milliseconds, and you checked it against seconds.. ;)


soja(Posted 2003) [#5]
Is there some other way to do this, without that much CPU load?

Yes. Use CreateTimer() and an event-based system.

Suggestion: Create a Timer with 1 Hz (one tick per second) and then on each Timer event ($4001, I think) just add a tick and check to see if however many seconds you want to have passed are passed.

As a side effect, your program does not have to halt execution while it's counting. It can continue on doing whatever, and your CPU usage will be very small.

Also, don't be worried about precidion if you're only counting in the minutes range. Precision only matter if you're worried about a few milliseconds here and there.


BachFire(Posted 2003) [#6]
Thank you to all of you! ;) Here is a slightly changed code, which works:

MainWindow=CreateWindow ("Reminder",GraphicsWidth()/2-100,GraphicsHeight()/2-75,200,150,0,5)
Global MinField=CreateTextField (10,25,160,20,MainWindow)
Global GoButton=CreateButton ("Go !",10,55,60,20,MainWindow)

Global target=0
Global mins=0

Repeat
 eid=WaitEvent()
 Select eid
 Case $803
 Exit
 End Select

 If EventSource()=GoButton
  If EventID()=$401
   Go()
  End If
 End If
Forever



Function Go()

target=MilliSecs()+((Int(TextFieldText(MinField))*60)*1000)

Repeat

If MilliSecs()>=target Then Notify "Time is up!":Return
Delay 150

Forever
End Function


As you can see, I pre-calculated the target-MilliSecs() number, and then just keep checking to see if it equals this, or if it's bigger than this. The Delay command ensures low CPU usage. Without the Delay thing, it uses 100% CPU consumption.

BUT.. A few seconds after I pressed Go!, the "Running" status in Win Task Manager, changes to "Not Responding". But it still works, and it's very precise (150 millisecs off, is okay..). How come, it's "Not Responding"..?


ashmantle(Posted 2003) [#7]
the Not responding thing might be because of the way you exit the function Go()..


try replacing :Return with :Exit

Exit() command exits loops..

then after Forever, place your Return function there..

I don't belive its wise to jump out of a function in a middle of a repeat-forever loop, because of how it handles the resources & such..


BachFire(Posted 2003) [#8]
Nah, that can't be it, because it gets nowhere near those commands, UNTIL the time is up. ;-) I tried it anyway, and it didn't work. Thanks anyway. ;)

I don't get why it's "Not Responding", in a simple Repeat-Forever loop..? Eek.. Maybe it's the Delay command. That command halts execution -> Not Responding.. Can that be it? That would NOT be good, because I use that command, to keep the CPU usage down.. :(


WolRon(Posted 2003) [#9]
[CODE]
SecondTimer = CreateTimer(1)
target=MilliSecs()+MinutesToWait*60000
repeat
WaitTimer(SecondTimer)
until MilliSecs()>target
[/code]


soja(Posted 2003) [#10]
I don't get why it's "Not Responding", in a simple Repeat-Forever loop..?

The answer is *because* you're in a repeat-forever loop. You see, the program is busy working between the Repeat and Forever, and you are not allowing it to even pay attention to events that Windows sends to it (like move, focus, resize, close, etc) -- so to Windows, it is unresponsive. This is what I meant up above where I said "your program does not have to halt execution"... I guess I said that wrong. Your program is still executing (of course) but Windows doesn't know about this.

What you have to do to fix this is make sure that WaitEvent is called in your loop. Whenever WaitEvent is called, Blitz (behind the scenes) starts looking for Windows event messages that Windows sends to it. You don't see this happen, but it does, and it's what keeps it responsive in Windows. It's also how you get the event messages.

I recommend an Event Loop like this:

CreateTimer(1) ; Timer that fires an event $4001 once per second
Target% = 5 ; 5 seconds -- change this to your textfield number
bCounting% = False ; has the user clicked the Go button?
tick% = 0 ; how long has it been since the user clicked Go?

While WaitEvent()
	Select EventID()
		Case $803 : End
		Case $401 : If EventSource() = BtnButton Then bCounting = True
		Case $4001 ; Timer event (once per second)
			If bCounting Then
				tick = tick + 1
				If tick = Target Then 
					bCounting = False
					tick = 0
					Notify "Target reached!"
				EndIf
			EndIf
	End Select
Wend



Insane Games(Posted 2003) [#11]
its 5:30am here, and i need some sleep.. so i dont know if i undertand right what you want.. but cant u just make a :

time = Input ("Enter time (in seconds) : ")

Delay time*1000

Print "wake up neo.."

?

ps. you cant do anything wile we're waiting.. but i guess it wont eat much cpu..


BachFire(Posted 2003) [#12]
Thank you to all who tried to help me. And thank you VERY MUCH to soja, for bringing me the help and inspiration, that helped me the most. I finished my program now. Thanks!

Get it at: http://bachfire.homepage.dk/btr1.zip


soja(Posted 2003) [#13]
And thank you VERY MUCH to soja...

Gosh, you're welcome. My pleasure. <blush> =)

Say, that's a pretty neat little "Laundry Timer" or "Egg Timer" program (as many people call something like that). I did find a couple small issues, though:

1) When the target is reached, and the notification is displayed, if the user clicks Cancel, the Start button is still shown as deactivated and the Stop button is still activated (even though the status is idle).

2) There's an interesting behavior when the user enters negative numbers... how long do you think it will count? =)
(It gets more interesting when you enter very large numbers, like 2^32-1, but that's no big deal...)


BachFire(Posted 2003) [#14]
Thanks again, soja! ;) Yeah, when used negative numbers, it required a patient man to handle the situation.. ;) These issues should now be fixed.

Get the updated version here: http://bachfire.homepage.dk/btr11.zip