+ Reply to Thread
Results 1 to 13 of 13

  Click here to go to the first Rift Team post in this thread.   Thread: Stackoverflow error

  1. #1
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default Stackoverflow error

    Based on the code below, I get a stack overflow error.

    What I think the problem is is that I "recall" the mail_open function if I'm "sleeping" or the queue isn't ready.
    Stacking more and more calls to the function.

    The reason I keep calling the function over is that if I don't the " test_sleep" function will proceed to the sleep even before I get to open the mail.

    My "print" debug would look like this

    Code:
     "before sleep"
     "after sleep"
     "Sleeping!"
     "Sleeping!"
     "Sleeping!"
     "Sleeping!"
       ...
       ...
       ...
     "Sleeping!"
     "Sleeping!" 
     "Sleeping!"
    Code:
    function sleep(sleep_time)
    	sleep_start_time = os.time()
    	sleep_end_time = sleep_time
    	is_sleeping_now = true
    end
    
    function isSleeping()
    	if sleep_start_time ~= nil then
    		is_sleeping_now = false
    		time_spent_sleeping = os.difftime(os.time(), sleep_start_time)
    		if time_spent_sleeping <= sleep_end_time then
    			is_sleeping_now = true
    			print("sleeping !")
    		else if time_spent_sleeping > sleep_end_time then
    				sleep_start_time = nil
    			end
    		end
    	end
    end
    
    local queueStatus = false
    local function QueueStatus()
    	queueStatus = Inspect.Queue.Status("global")
    	if queueStatus then return end -- global queue is still backlogged, var is true so exit out
    	queueStatus = false
    end
    
    function mail_open(mail_id)
       if not QueueStatus() then
          command.mail.open(mail_id)
       else
          mail_open(mail_id)
       end
    end
    
    function test_sleep()
       print("before sleep")
       mail_open("some_id")
       sleep(5)
       print("after sleep")
    end
    
    table.insert(Event.System.Update.Begin, {isSleeping, "myAddon", "SleepStatus"})
    How would I make sure the "after sleep" happens AFTER the actual sleep ?
    Last edited by Frostshizzle; 02-02-2012 at 07:18 AM.

  2. #2
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    Here's a test function I made that will better show what is happening.




    Code:
    function sleep(sleep_time)
    	sleep_start_time = os.time()
    	sleep_end_time = sleep_time
    	is_sleeping_now = true
    end
    
    function isSleeping()
    	if sleep_start_time ~= nil then
    		is_sleeping_now = false
    		time_spent_sleeping = os.difftime(os.time(), sleep_start_time)
    		if time_spent_sleeping <= sleep_end_time then
    			is_sleeping_now = true
    			print("sleeping !")
    		else if time_spent_sleeping > sleep_end_time then
    				sleep_start_time = nil
    			end
    		end
    	else
    		print("not sleeping")
    	end
    end
    
    
    function mail_open(mail_id)
    	if not is_sleeping_now then
    		print("Mail Openned ! " .. mail_id)
    		sleep(1)
    	else
    		mail_open(mail_id)
    	end
    end
    
    function test_sleep()
    	print("before sleep")
    	for i=1, 15 do
    		isSleeping()
    	end
    	mail_open("some_id")
    	for i=1, 25000 do
    		isSleeping()
    	end	
            print("after sleep")
    end
    
    test_sleep()

  3. #3
    Shield of Telara Adelea's Avatar
    Join Date
    Mar 2011
    Posts
    734

    Default

    Can you factor out what you do after the sleep?

    For example :

    Code:
    local amsleeping = false
    local sleeptime = 0
    local sleepfunc = nil
    local sleepstart = 0
    
    function MB.OnUpdate()
    	if not amsleeping then return end
    	local ct = Inspect.Time.Frame()
    	if ct - sleepstart > sleeptime then
    		print("Done sleeping...")
    		amsleeping = false
    		sleepfunc()
    	end
    end
    
    function MB.SleepDone()
    	print("In sleepfunc()")
    end
    
    function MB.Start()
    	sleepfunc = MB.SleepDone
    	sleeptime = 5
    	print("About to sleep...")
    	sleepstart = Inspect.Time.Frame()
    	amsleeping = true
    end
    
    table.insert(Event.System.Update.Begin, { MB.OnUpdate, "MB", "OnUpdate" })
    Here we have the Update.Begin event going off all the time, but if its not actually timing anything it just returns.

    Once you have a need to wait for something, set the various variables.

    Once the time is up, it will call the nominated function:

    /script MB.Start()
    [07:57:54] [/script] About to sleep...
    [07:57:59] [MB] Done sleeping...
    [07:57:59] [MB] in sleepfunc()

    If you need to wait inline in a function then that will be difficult, but if you can seperate out the before and after it becomes a lot simpler.
    Last edited by Adelea; 02-03-2012 at 12:05 AM.
    http://forums.riftgame.com/image.php?type=sigpic&userid=125779&dateline=13553  38065

  4. #4
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    I've stripped down the code to the essential (removed all the statistical data I track). Using this will result in a stack overflow error.

    Code:
    function sleep(sleep_time)
    	sleep_start_time = os.time()
    	sleep_end_time = sleep_time
    	is_sleeping_now = true
    end
    
    function isSleeping()
    	if sleep_start_time ~= nil then
    		is_sleeping_now = false
    		time_spent_sleeping = os.difftime(os.time(), sleep_start_time)
    		if time_spent_sleeping <= sleep_end_time then
    			is_sleeping_now = true
    			print("sleeping !")
    		else if time_spent_sleeping > sleep_end_time then
    				sleep_start_time = nil
    			end
    		end
    	end
    end
    
    local queueStatus = false
    local function QueueStatus()
    	queueStatus = Inspect.Queue.Status("global")
    	if queueStatus then return end -- global queue is still backlogged, var is true so exit out
    	queueStatus = false
    end
    
    local function mailOpen(k)
    	if not QueueStatus() and not is_sleeping_now then
    		Command.Mail.Open(k)
    		sleep(1)
    	else
    		mailOpen(k)
    	end
    end
    
    
    local function open_mailbox
     local status = Inspect.Interaction("mail")
    	if status == true then
    		-- get the list of email
    		mailList = Inspect.Mail.List()
    		for k,v in pairs(mailList) do
    			mailOpen(k)
    		end
           end
    end
    
    table.insert(Event.System.Update.Begin, {isSleeping, "myAddon", "SleepStatus"})

  5. #5
    Shield of Telara Adelea's Avatar
    Join Date
    Mar 2011
    Posts
    734

    Default

    I'm not really clear what you are trying to do - looks like opening mail or something.

    Anyway, this is something I wrote just now to open and take all attachments from sold auctions :

    Code:
    _QUEUEDJOBS = {}
    
    _OpenMail = {}
    local OM = _OpenMail
    
    local processing = false
    
    local pauseStart = 0
    
    function OM.OnUpdate()
    	if processing then return end
    	if #_QUEUEDJOBS == 0 then return end	
    	if Inspect.Queue.Status("global") then
    		ix, v = next(_QUEUEDJOBS)
    		if ix then
    			processing = true
    			if v.type == "OPEN" then				
    				Command.Mail.Open(v.id)
    				table.remove(_QUEUEDJOBS, ix)				
    				table.insert(_QUEUEDJOBS, 1, { type = "DETAIL", id = v.id })
    				table.insert(_QUEUEDJOBS, 1, { type = "PAUSE", len = 1 })
    				processing = false
    			elseif v.type == "DETAIL" then	
    				md = Inspect.Mail.Detail(v.id)
    				table.remove(_QUEUEDJOBS, ix)
    				for _,i in pairs(md.attachments) do
    					table.insert(_QUEUEDJOBS, 1, { type = "TAKE", id = v.id, aid = i })
    				end
    				processing = false
    			elseif v.type == "TAKE" then
    				Command.Mail.Take(v.id, v.aid)
    				table.remove(_QUEUEDJOBS, ix)
    				processing = false	
    			elseif v.type == "PAUSE" then
    				if pauseStart == 0 then
    					pauseStart = Inspect.Time.Frame()
    				else
    					local curTime = Inspect.Time.Frame()
    					if curTime - pauseStart > v.len then
    						pauseStart = 0
    						table.remove(_QUEUEDJOBS, ix)
    					end
    				end
    				processing = false
    			end
    		end
    	else
    		print("Q busy...")
    	end
    end
    
    function OM.HandleMail()
    	if not Inspect.Interaction("mail") then
    		print("Mailbox not open..")
    		return
    	end
    	for k,v in pairs(Inspect.Mail.Detail(Inspect.Mail.List())) do
    		if string.sub(v.subject,1, 16) == "Auction Sold for" then
    			table.insert(_QUEUEDJOBS, { type = "OPEN", id = k })
    		end
    	end
    end
    
    function OM.SlashHandler(args)
    	local r = {}
    	local numargs = 0
    	for token in string.gmatch(args, "[^%s]+") do
    		r[numargs] = token
    		numargs=numargs+1
    	end
    	if numargs>0 then
    		if r[0] == "start" then
    				OM.HandleMail()
    			return
    		end
    	end
    end
    
    table.insert(Command.Slash.Register("om"), { OM.SlashHandler, "OpenMail", "SlashHandler" })
    table.insert(Event.System.Update.Begin, { OM.OnUpdate, "OpenMail", "OnUpdate" })
    Open the mailbox, and type /om start

    I had to break it up into several different strands, and had to pause before getting a single mail detail, else it wouldnt populate the return table properly (no attachment detail), but adding in a delay before trying to determine attachments solved that.
    http://forums.riftgame.com/image.php?type=sigpic&userid=125779&dateline=13553  38065

  6. #6
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    Quote Originally Posted by Adelea View Post
    I'm not really clear what you are trying to do - looks like opening mail or something.
    Well, after opening the email I do parse the content (to see if the mail comes form the AH, if so determine if its sold/expired) and finally take any attachment.

    But since only "opening" the email is enough to reproduce the issue I only kept that part in the example I gave above (for the sake of clarity)

  7. #7
    Shield of Telara Adelea's Avatar
    Join Date
    Mar 2011
    Posts
    734

    Default

    Well take a look at what my code is doing - see if it helps you at all.

    I'm sure there are cleaner/better ways of doing this, but this is what worked for me.

    Doing a pause in the middle of a function isnt something that is going to work well, and neither is recursively calling yourself!
    http://forums.riftgame.com/image.php?type=sigpic&userid=125779&dateline=13553  38065

  8. #8
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    Doing a pause in the middle of a function isnt something that is going to work well
    This seems to be a specificity to Lua that I will need to get accustomed to. I've made pauses in other languages before without issues. I don't mind it, I just need to find a way to deal with it, that's all.

    A big thanks for you help anyway. I'll see to refactor my code to bring it in line with your proposed changes.

  9.   This is the last Rift Team post in this thread.   #9
    Rift Team
    Join Date
    Oct 2010
    Posts
    927

    Default

    Quote Originally Posted by Frostshizzle View Post
    This seems to be a specificity to Lua that I will need to get accustomed to. I've made pauses in other languages before without issues. I don't mind it, I just need to find a way to deal with it, that's all.
    There actually *are* ways to do this, but they take a little black magic. For example, maybe you want this code to do what it looks like it should do:

    Code:
    local function Process()
      for i = 1, 100 do
        print(i)
        Pause(1)
      end
    end
    Using the magic of coroutines, this is possible:

    Code:
    local function Pause(seconds)
      local start = Inspect.Time.Frame()
      while start + seconds > Inspect.Time.Frame() do
        coroutine.yield()
      end
    end
    
    local function Process()
      for i = 1, 100 do
        print(i)
        Pause(1)
      end
    end
    
    local coro = coroutine.wrap(Process)
    table.insert(Event.System.Update.Begin, {coro, "TrionDevelopmentTools", "OnUpdate" })
    It takes a little extra work, and I strongly recommend researching coroutines *heavily* before using them extensively, but it lets you write really deliciously clean code if you're willing to put the support structures around it and be extremely careful.

    (For example, this code starts erroring every frame once it's done with its count. You'll have to do better to make it work right.)
    Last edited by ZorbaTHut; 02-03-2012 at 12:32 PM.

  10. #10
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    Very interesting !

    Now I wonder which one of the two path I'm going to take.

    I think I'll first dab into the coroutine option, despite the fact that it look harder to implement. If I can't get it to work I'll switch to the "jobs_queue".

    A big thanks to both of you!

  11. #11
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    I managed to get the coroutines working just fine for the email opening / recording.

    Now I have to rework my "processing", as it behaves very erratically now.


    is it me or the coroutines can fail silently ? (which would explain why my mail processing doesn't seem to work at times)

  12. #12
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    Quote Originally Posted by Frostshizzle View Post
    I managed to get the coroutines working just fine for the email opening / recording.

    Now I have to rework my "processing", as it behaves very erratically now.


    is it me or the coroutines can fail silently ? (which would explain why my mail processing doesn't seem to work at times)
    Nailed the processing part as well as taking out the attachments. A few optimization here and there and I'll have a pretty solid email management system for the add-on. This will leave posting before getting to the "meat" of the add-on.

    A BIG thanks to people that helped me.

  13. #13
    Rift Disciple
    Join Date
    Mar 2011
    Posts
    130

    Default

    Also, quick unrelated question, is the PTS "Down" ? I didn't see any NPCs when I logged in during lunch time today. So I couldn't post anything.

+ Reply to Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts