ID:112620
 
Not a bug
BYOND Version:481
Operating System:Windows 7 Ultimate 64-bit
Web Browser:Internet Explorer 8.0
Applies to:Dream Daemon
Status: Not a bug

This is not a bug. It may be an incorrect use of syntax or a limitation in the software. For further discussion on the matter, please consult the BYOND forums.
Descriptive Problem Summary:
If I spawn in World/New() before calling a proc that loops infinitely, profiling will reveal the proc has an apparently real-time of hundreds of millions of seconds. Sometimes negative that amount. It also does this if the spawn is in the loop itself. The problem is, however, that after about a day and a half, the proc crashes. I think the more the loop does, the faster the proc crashes, but it takes too long for the loop to crash for me to experiment with this.

Edit: Just the proc crashes. Not Dream Daemon. There are two loops I use in my actual code: One updates all mobs' "status" variable every two seconds, and one backs up the world every half hour. Both crash eventually, within about an hour of each other, and there is no error output to Dream Daemon when they do crash. I can only tell they've crashed because they stop doing anything. They're separate loops, so it's likely not a problem with my code. I'm pretty sure it has to do with the profile thing described above, and also below.

Numbered Steps to Reproduce Problem:
Compile the code below until the comment into a blank, new environment in Dream Maker, run the world in debug mode, and go to F1->Server->Profile and you'll see what I'm talking about.

Edit: Or look at this picture, if you don't feel like trying it.

Ignore the "Infinite Loop" function in the table. I've removed it from the below code snippet and it does not matter.
Code Snippet (if applicable) to Reproduce Problem:
Here's what I used to test it:

world/New()
spawn() infiniteLoopThatSpawns()
otherInfiniteLoopThatSpawns()

proc/infiniteLoopThatSpawns()
sleep(20)
spawn() infiniteLoopThatSpawns()

proc/otherInfiniteLoopThatSpawns()
sleep(20)
spawn() otherInfiniteLoopThatSpawns()

//Or fine. My actual loop. A little less simple.
world/New()
..()
//Sometimes I spawn before while(1) and sometimes I don't. It crashes eventually either way.
while(1)
sleep(20)
for(var/mob/M in world) if(M.client)
var/bar = winget(M, "MainWindow.input", "text")
if(bar == "" || bar == "Say \"" || bar == "Emote \"") M.status = ""
else if(findtext(bar,"Say",1,4) || findtext(bar,"Emote",1,6) ) M.status = "Typing..."
else M.status = ""
if(M.client.inactivity >= 6000) M.status = "Idle"


Actual Results:
The first one, infiniteLoop(), never returns, so it has a realtime of zero, but the other two in the test I'm running right now have a realtime of negative 700M seconds. This doesn't happen if the proc called isn't an infinite loop, or if there is no spawning involved.

I've tried making an infinite loop without spawning at the end of world/New, like doing
world/New()
while(1)
doStuff()
but it still crashes eventually. It takes about a day and a half for world/New to crash when its infinite loop loops every half hour, so I figure it's probably a realtime issue instead of a stack overflow issue.

Does the problem occur:
Every time? Or how often?
Every time. But changing anything in the code tends to change how many hundreds of millions of seconds of runtime the procs have.
In other games?
Yes, I've reproduced the problem in a test environment
In other user accounts?
Untested
On other computers?
Yes. I've tried it on a Windows XP machine, as well. I've also tried it in version 479. The XP machine has 479, and my laptop did until I upgraded to make sure this wasn't caught and fixed. I test it in Dream Seeker on my laptop and Dream Daemon on the XP machine.
When does the problem NOT occur?
I can't really find a way to make an infinite loop start upon world/New() without it crashing after an hour and a half.

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked?

I use infinite loops a lot, especially with games and programs that are hosted for days or weeks at a time, and have never noticed them crashing before. And world/New() the ideal place to call them from. Basically, I noticed my infinite loop in this project was crashing with no error, so I checked profile and noticed, "Hey, wait. My server hasn't been up for 130 million seconds. Only like eight hours. That can't be right."

Workarounds:
I dunno. I haven't tried making a verb call the infinite loop, but that might work. If I do that, it doesn't give them a hundred million runtime seconds, but they might still crash later.
Its somewhat irrelevant to this issue, since I believe the timing problem occurs either way, but using while(1) is more efficient than recursively calling infinite loops.
The infiniteLoop() proc in your example will eventually crash due to stack overflow. That is a bug in your code. You need to take that out of your example and see if the problem persists.

You didn't provide any details on how this crashes, whether it's a proc crash or if Dream Deaemon itself crashes. I do know that your infinitLoop() proc will produce a proc crash at some point.
Just the procs crash; not Dream Daemon. I'll add that up there somewhere. The infinite loop I actually use is more like the third one, but I've tried it tons of different ways, including
world/New() spawn() infiniteloop()
infiniteloop() while(1)
sleep(20)
//Do stuff

But I don't think I have loop_checks set to zero, so it should catch it before a stack overflow anyway.
You should edit your report to include only the loop type you're actually using then. Having the extraneous info muddies the waters. As it is there isn't really anything in the report that can be tested.
Well, the loops I posted do have the weird realtimes in Profile, but I didn't run them and sit here for two days and make sure they crash, so sure, I posted one that I do know crashes.
I don't know what you mean by that; you posted three variants, not one. Just pull out the code that you're not using so it's clear. There's already nothing to investigate but the lack of clarity is making the issue even worse.
But there's plenty to investigate. And I posted variants because it all does it. I gave instructions on how to duplicate the result with a skeletal function because I already made sure it wasn't just my code. Just take everything from world/New() until the comment, put it in Dream Maker, set debug mode, hit Ctrl+R, let it run, go to Profile in Dream Seeker, and look at what it does.

No, I don't know for certain that all of the example loops will crash, but I didn't think you'd wait a day to see if they'd crash, either. I just made sure they had huge and/or negative values for real time usage in Profile. But the loop after the comment is one I actually use, and it crashes for certain after about a day and a half, so you could investigate that one.

I'll add that picture to the original post, I guess, since you don't seem to have tried it.

Edit: Also, my real loop won't show up in the profiler because it never returns like the recursively-called loops do. But it still crashes, and it shouldn't. Also also, if you replace the "sleep(20)" into the two "infiniteloopthatspawns" procs with the contents of the while loop in my real code, I've tried that, and it crashes, and it has the same problem in the profiler.

Also also also, I removed the first "infiniteLoop" function from the code because I guess it doesn't help at all. But the other two do. They shouldn't ever crash, and they shouldn't have a billion seconds of real time the moment a world is created, but they do. Except, again, I haven't tested to see if those exact functions crash because they take two days to do it. I can test it if you really, really want me to. But I've said that if you replace the sleep(20) with my real loop, that it does crash and I've seen it happen.

Specifics are so very important.
The reason I say there's nothing to investigate is that for obvious reasons, we can't reproduce a bug if it takes a day and a half to appear. The profile issue actually is possible to investigate, and I will, but the proc crash--which is really the bigger deal--is something we need a lot more info on. That's really my main focus; the profile thing has been around for a long time and while it's annoying it doesn't appear to be hurting anything. It is almost definitely unrelated to your proc crash, so while I'll look into it I'm not as concerned about it.

I'm not sure what you mean about not having tested "those exact functions" since I'm not sure which functions you're referring to. As you said, specifics are very important. I need to know which specific code you have tested that crashes the proc, and if you haven't tested any besides the version that has the stack overflow then you need to do so.

I'm also not sure what you mean about replacing sleep(20) with your "real loop". What real loop are you referring to? If you mean the while(1) bit in the currently posted code, there's an issue in that you're never spawning out of world/New() and so world/New() never finishes. It would be clearer to give specific examples of which code causes the crash, and which doesn't.
I'm just trying to explain how many different ways I have tried coding this loop, and it still crashes every way I try it. Whether or not I spawn before calling the loop, and whether the loop is just while(1) or if I make a function spawn and call itself recursively. I can't remember if I've tried compiling without debug mode checked. Probably not. I did try it once without starting the profiler, though, and it still crashed.
I've found something in my "test environment" that crashes much faster than that. Excuse the first three proc calls in my world/New; I'm referring to the rest of the proc, but if I comment out the other three loops, the other one doesn't crash nearly as quickly. Here's everything that happens in world/New() in that environment:

world/New()
..()
spawn() infiniteLoop()
spawn() infiniteLoopThatSpawns()
otherInfiniteLoopThatSpawns()
spawn() while(1)
sleep(20) //SLEEP.
for(var/mob/M in world)
M.setTyping()

//This looks like "my real loop" above, because it's an older version of the same function.
mob/proc/setTyping()
var/bar = winget(src, "mainwindow.input", "text")
if(bar == "" || bar == "Say \"") typing = 0
else typing = 1
if(src.client.inactivity >= 200) typing = 2

//These sleep for less time than the ones above.
proc/infiniteLoop()
//This crashes after a thousand calls because of a stack overflow.
sleep(3)
infiniteLoop()
proc/infiniteLoopThatSpawns()
sleep(3)
spawn() infiniteLoopThatSpawns()
proc/otherInfiniteLoopThatSpawns()
sleep(3)
spawn() otherInfiniteLoopThatSpawns()


There's a lot of "unimportant" stuff in that, but if I comment it out, it doesn't crash as fast. What this code does is, the while(1) loop inside world/New() crashes after about five minutes. I check this by using the profiler to see when setTyping() stops getting called. As before, there is no error when the loop crashes. I only know it's no longer looping because setTyping() isn't being called anymore.

With the code as I've posted it, the "setTyping" proc stops getting called after about 140-150 calls, so after about five minutes. Much sooner than half a day. However, if I modify the value inside the sleep() that I have flagged with "//SLEEP.", it still crashes after about five minutes. If the value is 20, the function gets called 140-150 times. When I changed it to 50, the function was called 60 times. When I changed it to 200, it was called 15 times, and when I changed it to two it was called 1482 times.

So whatever's crashing the loop, it doesn't matter how many iterations the loop goes through. It seems to either have to do with how long the loop is running, or how long it sleeps for.
In the interest of keeping things clean, I want you to post a new bug report for the procedure crash. This one is for the profiler.

In the new bug report, isolate the smallest set of code for us to run in order to re-create your issue. Running examples "like your code" will not do, we need a precise set of code (as small as possible) that crashes for you, so we can just pick it up, run it, and see it crash as well.
While I would have suggested the same before 482's release, in the interest of expediency and clarity I already posted and resolved an issue for the profiler thing. I think we can narrow this bug report to just the soft-code "crash", but it definitely does need to be pruned of all the info that isn't relevant, and it needs specific code that always causes the problem.
I have posted a new bug report with only the code I'm using right at this very minute, even though I've told you every loop I've posted in this thread does crash and I have tested it, and one of them will even crash for you in five minutes instead of five hours. But sure.
The proc timing issue has been dealt with in another report and the spawn thing has been moved to another report as well, so this one is now redundant.