Possible that Update() is multi-threaded?

I'm getting a REALLY strange exception. It only occurs when I run my game via Ctrl-F5 (rather than F5) and even then I can't always reproduce it. It's in a bit of code that is simply a foreach-loop on a List<> of Particles and calling Update(elapsed) on each of them. The error occurs on the first line of Particle.Update() which is "base.Update(elapsed)" which calls the Sprite class' Update()... which has no indexes in it.

At the end of the loop I look for any particles that need to be removed from the list and do so. Based on what little debug information I can get, it looks like it's an internal error in System.Collections.ListDictionaryInternal... Count is zero, which pretty much should never be the case... there's always numerous particles on the screen when the exception is thrown.

Because the framerate increases by an order of magnitude or so when I use Ctrl-F5, I'm wondering if Update() is running multiple times at once, deleting particles from the list that another thread is attempting to iterate

I can't post the entire code here (multiple files and dependent texture files) but I can package it up and send it to someone if needed.

System.IndexOutOfRangeException was unhandled
Message="Index was outside the bounds of the array."
Source="First"
StackTrace:
at First.Particle.Update(Single elapsed) in q:\XNA\First\First\Sprite.cs:line 179
at First.PlayComponent.Update() in q:\XNA\First\First\PlayComponent.cs:line 331
at Microsoft.Xna.Framework.Game.UpdateComponents()
at First.Game1.Update() in q:\XNA\First\First\Game1.cs:line 96
at Microsoft.Xna.Framework.Game.Tick()
at Microsoft.Xna.Framework.Game.HostIdle(Object sender, EventArgs e)
at Microsoft.Xna.Framework.GameHost.OnIdle()
at Microsoft.Xna.Framework.WindowsGameHost.ApplicationIdle(Object sender, EventArgs e)
at System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32 grfidlef)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at Microsoft.Xna.Framework.WindowsGameHost.Run()
at Microsoft.Xna.Framework.Game.Run()
at First.Program.Main(String[] args) in q:\xna\First\First\Program.cs:line 14



Answer this question

Possible that Update() is multi-threaded?

  • eshny

    Is it possible for you to upload the game somewhere If not, can you email it to me and I'll take a look

  • db_guy

    BTW, you can see a screenshot of the debug details here:

    http://ilocker.bsu.edu/users/mstum/world_shared/xna_exception.gif


  • Vaassu

    Hey Matt,

    maybe I'm wrong but I've had a similar strange behaviour like your problem some time ago.

    I did some collision detection stuff with a list of objects, I walked through the list with a
    foreach-statement and removed the objects that tested positive and had a very similar
    Error like you got.
    I can't remeber if the code was multithreaded or not, but I believe it wasn't.

    I fixed this problem by doing the foreach-loop and added the positive-tested objects to a
    temporal second list and used this list for removal ob the objects from the original list.
    This solved my problem and the error was gone.

    I guess the foreach-statement builds an iterator to walk through the list and if you remove
    an element the iterator gets corrupted because it's built at the beginning of the loop and
    then you get an error......

    Try it out, I think it works....let me know....


    Have fun.....

  • JustStudent

    Found this recently.....

    very neat solution :-)

    thx .net




  • James Strickland

    Don't forget that you can post game projects up on www.codeplex.com!



  • Peter Feigl

    Very nice. I'm filing that piece of code away for future reference.

  • aschaeffer

    Shellacc wrote:
    Hey Matt,
    I guess the foreach-statement builds an iterator to walk through the list and if you remove
    an element the iterator gets corrupted because it's built at the beginning of the loop and
    then you get an error......


    Exactly. The CLR does not allow you to modify a collection at the same time as you are iterating over it, so you can't add or remove elements from inside a foreach loop.


  • roy-roy

    I *may* have found the problem. Further down in the Particle.Update routine I found a bad piece of logic that could exceed a Color[] array index. I've been running this code off and on for the better part of the week with a constant 600+ particles at all times without any problems... the logic error is based on such a rare timing issue that it just never surfaced. However, when I ran it via Ctrl-F5 instead of F5 the FPS went from 30 to 200. I think it took the smaller time slices to bring out the problem (even though it was still hard to replicate).

    I've fixed the logic error and I haven't seen the exception yet, although I'm not ruling anything out. It's just odd that the stack dump is so misleading.

    For now I'll consider the case closed and let you guys get on to more important things. If the problem resurfaces I'll give this thread a bump and publish the entire source package.


  • Santhoo

    Stupid forum errors! You can email it to me at machaira-at-comcast-dot-net.

  • Sandeep A

    I ran into the same thing, but this is quite a different problem. If you look at my earlier code snippits above you'll see where I iterate through each of the particles and then call the following line:

    if (p.delete) dead.Add(p);

    I then rip through all of the items in "dead" and remove them from "particles". I then call p.Dispose() on each of the particles. Once I'm done I call dead.Clear();

    But no, that problem shows up each and every time... easy to replicate. What I ran across was a stack dump that wasn't really telling me where the offending line was... that made it a LOT harder to track down the real problem (assuming I've fixed it).


  • BilalShouman

    Here's the snippet in my PlayComponent that is ripping through all of the particles:

    foreach (Particle p in particles) {
    p.Update(elapsed);
    if (p.delete) dead.Add(p);
    }

    particles and dead are both List<Particle>

    The line that actually throws the exception is in Particle.Update. Particle is derived from Sprite.

    public override void Update(float elapsed) {
    base.Update(elapsed); // <=== This is the line that throws the exception
    age += elapsed;
    // ... etc ...

    And just for reference, here's the entire contents of Sprite.Update:

    public virtual void Update(float elapsed) {
    if ((acceleration.X != 0.0F)||(acceleration.Y != 0.0F)) {
    velocity +=
    Vector2.Multiply(acceleration, elapsed);
    }
    if ((velocity.X != 0.0F) || (velocity.Y != 0.0F)) {
    position +=
    Vector2.Multiply(velocity, elapsed);
    }
    if (roationspeed != 0.0F) {
    rotation = (rotation + ((roationspeed *
    MathHelper.TwoPi) * (elapsed / 60.0F))) % MathHelper.TwoPi;
    }
    }


  • wva

    Very mysterious! That code all looks fine to me.

    Hopefully someone else will come along and spot the obvious error that we're both missing, and make us look stupid :-)


  • MicahN

    The CLR doesn't do any magic threading behind your back, so that can't be the problem.

    It would be useful if you could post the source for the method that is actually throwing the exception (First.Particle.Update(Single elasped)).


  • ZopoStyle

    The same solution is used in SpaceWar for removing bullets and particles etc from the scenegraph. See SceneItem.cs, I guess its not all bad design after all :-)

  • Possible that Update() is multi-threaded?