It looks like you're new here. If you want to get involved, click one of these buttons!
I wandered (more blundered ) into something with coroutines which threw me a little for a few minutes (a while
) so I thought I'd write a little about debugging. I couldn't see something like this on here from google or the custom search so i thought I'd post it but apologies if i am repeating an existing post
So essentially the upshot of my (super fun...) time spent trying to figure out what was going on with my coroutine was: coroutines swallow error messages and won't print them to the output unless you handle errors and print them to the output yourself
So if you have any potentially error prone code in a coroutine it'll hopefully save you a few minutes by wrapping it in an xpcall, or pcall to get a stack trace (using debug.traceback()) or at least by knowing the point where the error occurred-ish by printing your own error message
I find debugging is not exactly an exact science and can sometimes be more guessing your way up the call stack until something jumps out at you and hits you in the face with a frying pan so essentially patience is required, you'll get there eventually
. It can sometimes even require many iterations of commenting one line out, running your application, test and repeat until the culprit (or culprits) are found.
Here's some examples of using xpcall and pcall to catch errors and get a stack trace when an error occurs
xpcall stack trace - probably quite a long call chain in a real use case.
local funcWithError = function() return nil_table[1] end
local r= coroutine.create(function() xpcall(funcWithError,function() print(debug.traceback()) end) end)
coroutine.resume(r)
-- instance output
stack traceback:
[string "ClassName = class()..."]:14: in function <[string "ClassName = class()..."]:14> -- first call /entry point here
[string "ClassName = class()..."]:13: in function <[string "ClassName = class()..."]:13> -- error occurred here
[C]: in function 'xpcall'
[string "ClassName = class()..."]:14: in function <[string "ClassName = class()..."]:14> -- error caught here
-- global output (from in setup())
stack traceback:
[string "-- ProjectName..."]:6: in function <[string "-- ProjectName..."]:6> -- first call / entry point here
[string "-- ProjectName..."]:5: in function <[string "-- ProjectName..."]:5> -- error occurred here
[C]: in function 'xpcall'
[string "-- ProjectName..."]:6: in function <[string "-- ProjectName..."]:6> -- error caught here
pcall stack trace - this is not as detailed as xpcall because it has destroyed the part of the stack (specifically the bit where the error occurred) when it returns true or false for if the call was successful.
In general using just pcall does not seem to give such a nice/detailed stack trace because the stack has already unwound when the function returns but it could be a good starting point for working backwards manually
.
However the callback passed to xpcall is different, this is called before the stack unwinds so this usually has more detail because the call to print(debug.traceback()) is made before xpcall returns and the stack where the error occurred is still available.
local funcWithError = function() return nil_table[1] end
local r= coroutine.create(function() if not pcall(funcWithError) then print(debug.traceback()) end end)
coroutine.resume(r)
-- instance output
stack traceback:
[string "ClassName = class()..."]:14: in function <[string "ClassName = class()..."]:14>
-- global output
stack traceback:
[string "-- ProjectName..."]:7: in function <[string "-- ProjectName..."]:7>
And finally (the clean version of) quoting myself scratching my head earlier... "Just what happened here did it work , I don't know the output says nothing so I'm going with yes..."
Nope because the error was eaten up inside the coroutine. Although you can receive a status like pcall from the first return value. False for something went wrong True for all went fine . A call to print(debug.traceback()) will probably return similar results to that of pcall when ok is false.
local funcWithError = function() return nil_table[1] end
local r= coroutine.create(function() funcWithError() end)
local ok = coroutine.resume(r)
-- nothing printed in output
Last but not least some tips for reading stack traces:
Follow your stack traces from the bottom up, the top is the starting point of current the code path, the bottom is where the error emerged (was caught) so following it from the bottom up should take you on a shorter route to the call where the error occurred
You will see some numbers in the stack traces these are line numbers corresponding to the path the code follows/flows through your source files.
Absolutely finally I must say this is by no means a definitive debugging guide, but I hope this will save some headaches when it comes to debugging coroutines or just debugging in general
Comments
Quite interesting thank you!
I had no idea on how to use debug tools of lua, now your post give me an entry point.
Thanks @Jmv38, glad you liked it
debug info may look like a whole lot of nonsense at a glance and it does take some getting used to but once you get the hang of it, it's invaluable when investigating problems in your code 
@XanDDemoX - what you are saying is really interesting, but it is probably going over a lot of heads, including mine.
I saw there was a pcall and xpcall function in the Lua documentation, but wasn't sure how to use it. Nor the debug.traceback function.
I'm going to write something about this, and when I do, would you mind checking it for me?
Certainly @ignatz. Essentially the bottom line is that the callback in xpcall is called just after your code fails, which is where all the information about the error is because the callback is called within the same scope as xpcall (inside xpcall). Well as in :
Edit: Also to be super clear: you can use xpcall or pcall anywhere you need to handle errors which could potentially happen like converting a string value entered by the user to an integer ( which in lua I think is tonumber()) and then trying to use it as a number when it's value is actually "nil". (Which will happen if the user enters something which isn't a valid number and will cause a runtime error )
Thank you @XanDDemoX I was having an issue tracing errors in my coroutines earlier today! Time to play with this some and get a better understanding of how it works!
Thanks @Ignatz and @Briarfox. Glad you have found it useful
hopefully my pain has been your gain 
I'm still trying to wrap my head around coroutines. Does coroutine.wrap() work in codea? I keep getting an error.
If you figure that one out, let me know!
I don't even know what coroutines are...
@SkyTheCoder- try this.
http://coolcodea.wordpress.com/2013/03/25/coroutines-or-how-to-report-progress-of-a-big-job/
@Briarfox , @Ignatz I haven't really used them either but wrap certainly does work in codea
here's a wrap example to try out, it's a iterator that always counts at the increment given when its created but can then also count an extra amount on a per call basis so the result from yield will be:
Edit: Also to note in this example the value calculated from the previous call is returned on the next
Edit 2: If you need the value to be returned on the current call then perform the calculation before yield.
(I think I may have inadvertently combined a couple of examples I've seen floating about on google for this
)
Cool, thanks
Awesome, thank you!
Here's an extension of the example above
which hopefully shows a more practical use for the initial values
this can count backwards or forwards based on whether you pass a negative or positive number to the call to c() but it will stop counting when it reaches min or max 
Here is an exemple of wrap i made for myself to understand how to use coroutines. It shows i can do a long function that take may seconds but still can have a real time screen update (the progress bar and number showing).
How to you restart a coroutine.wrap()? Once it hits dead it seems to stay dead...
@Jvm38 nicely done
, looks like your really getting the hang of it!
.
@Briarfox it needs to be kept alive inside the function your "wrapping" or re-created as required depending on your requirements
. Coroutines are mainly used to do long tasks (but they do have some other interesting uses, i saw an example somewhere where one was used for inversion of control).
They usually require some sort of loop and then you yield in between iterations to do other tasks or update the user interface with progress for example. A loop will keep your coroutine alive until the condition which stops the loop is met. Above I've kept mine alive indefinitely using an infinate loop:
But you could use just a normal "for" loop to iterate through a large collection performing complex calculations, for example, or "repeat until" as well
Coroutines will "die" upon error or falling out past the functions "end" so the trick is to keep it "suspended" inside your wrapped function until you no longer need it / your long running task is complete, whilst yielding to do other things
@XanDemoX Think I figured it out. I do not believe you can restart a coroutine.wrap once it has died. Because if you call it, it returns that it's dead. I couldnt figure out a way to restart it.
I ended up just using create and it fixed my issues. Thank you all for the help and sharing code
@Briarfox, my apologies i wasn't massively clear on that point
, you are 100% correct, you can't resurrect one once its dead (but you can stop that happening
)