Rarely, I get this error when calling Run:
System.ArgumentException: An element with the same key already exists in the dictionary.
at System.Collections.Generic.Dictionary`2[OpenTK.ContextHandle,System.WeakReference].Add (ContextHandle key, System.WeakReference value) [0x00000] in <filename unknown>:0
at OpenTK.Graphics.GraphicsContext..ctor (OpenTK.Graphics.GraphicsMode mode, IWindowInfo window, Int32 major, Int32 minor, GraphicsContextFlags flags) [0x00000] in <filename unknown>:0
at OpenTK.Platform.Utilities.CreateGraphicsContext (OpenTK.Graphics.GraphicsMode mode, IWindowInfo window, Int32 major, Int32 minor, GraphicsContextFlags flags) [0x00000] in <filename unknown>:0
at OpenTK.Platform.Utilities.CreateGraphicsContext (EAGLRenderingAPI version) [0x00000] in <filename unknown>:0
at OpenTK.Platform.iPhoneOS.iPhoneOSGameView.CreateFrameBuffer () [0x00000] in <filename unknown>:0
at OpenTK.Platform.iPhoneOS.iPhoneOSGameView.RunWithFrameInterval (Int32 frameInterval) [0x00000] in <filename unknown>:0
at OpenTK.Platform.iPhoneOS.iPhoneOSGameView.Run () [0x00000] in <filename unknown>:0
at WAML.IOS.RenderBoxImplementation.Start () [0x00000] in <filename unknown>:0
I haven't been able to repro it, but it's happened quite a few times. It seems to be random. Saw in mtouch 18.104.22.168 (8d98f5e).
Googling it showed someone else that ran into this problem too, but his post to the forums did not receive any replies: http://monotouch.2284126.n4.nabble.com/Modification-of-the-OpenGLESSample-GameView-td4615982.html
Would you happen to have a test case we can use to reproduce this?
Reading the source code, and based on your comment that this happens randomly, I wonder if you are using threads in your application, and just two threads try to create the GraphcisConext at once?
Okay, I spent some more time on this, and I can now provide a repro. See attachment.
And no, it doesn't appear to be related to multi-threading. My app only ever calls Run from the UI thread, as does the attached sample.
The attached sample is just a simple app that creates and destroys iPhoneOSGameViews rapidly. If you let it run long enough on an actual device, the above error happens (usually within 15-30 seconds for me.)
Created attachment 3982 [details]
Notice that NSTimer runs on a parallel thread to the UI thread, so this means that you are calling the new UIViewController from a background thread.
Can you rename your Tick () function with Tick2 (), and then make Tick () be:
BeginInvokeOnMainThread (() => Tick2 ());
I don't believe that is true. If I write out the Thread.CurrentThread.ManagedThreadID from a NSTimer callback, it always shows it's on the UI thread.
Nevertheless, I tried using BeginInvokeOnMainThread anyway as you suggested and I still got the same error.
Sebastien, would you mind researching this issue?
I can duplicate it and it does not seems to be threading related.
The sample creates a lot of new GraphicContext and the same context pointer can be (re)used.
Right now the code does not check if the WeakReference is alive before adding it to the dictionary. If it's not alive it should replace the existing entry. That's the "direct" cause of the exception.
Now if the WeakReference is not alive then it must have been disposed - and that should have removed the entry from available_contexts. OTOH my breakpoint on Dispose was not hit - and that's likely the "real" bug.
The main dispose issue is the lack of a finalizer, making instance freed by the GC (and not manually disposed), not removed from the dictionary.
removes entries from `available_contexts`. That makes the situation better but it's still possible that the pointer is part of the dictionary (but it's weak reference will be alive). Looking into that...
The old, still in the dictionary, `implementation` seems invalid (disposed?) and that's where the Context handle comes from (not from the object instance `this` on which the weak reference is kept).
So it seems the tracking is not done on the "correct" data and can introduce a race situation like this one, i.e. the inner `implementation` is disposed but it's parent is not (yet). So the handle can be (and is) reused and fails to be added.
One "hacky" solution would be to ignore it, IOW replace any existing (half-disposed) instance with the new (fresh) one.
The "right" one would be to track the right data, the `implementation`, but might be more invasive. Looking into it...
Sometime the GraphicsContext dispose of the EAGLContext (in it's Dispose method) and sometime the EAGLContext is disposed before (e.g. when DestroyFrameBuffer is called).
That means the previous (accessed thru the WeakReference) `implementation`, which is a iPhoneOSGraphicsContext, has it's EAGLContext (it's Handle is 0x0). Now `implementation.Context` is a *copy* of `contextHandle` - the original Handle value (IntPtr).
So that makes it possible, on the native side, to reuse the same handle (it's been freed) while the managed side still as a copy of the value (contextHandle). That happens when the GraphicsContext has not yet been disposed and it means it's possible to re-add the same handle value into the dictionary.
Cleaning that `contextHandle` (when disposed) value does not work since it's used to remove entries from the dictionary (so the problem remains).
The second bug seems to dispappear when we ensure the dispose order remains identical everywhere (I'm well over 10k instances).
Part's of GraphicsContext Dispose job is to call EAGLContext.Dispose so this ensure they get released in the "right" order (so the dictionary is kept in sync).
OTOH maybe I'm missing the point why only the EAGLContext was disposed and the GraphicsContext only null'ed ?!?
diff --git a/src/OpenGLES/OpenTK_1.0/Platform/iPhoneOS/iPhoneOSGameView.cs b/src/OpenGLES/OpenTK_1.0/Platform/iPhoneOS/iPhoneOSGameView.cs
index 7bb7d03..e757940 100644
@@ -557,7 +557,7 @@ namespace OpenTK.Platform.iPhoneOS
GraphicsContext = null;
gl = null;
The first bug (missing finalizer), inside opentk, has been filled as http://www.opentk.com/node/3330
Fixes for the two issues are now fixed in 938cf69518205d26fe4243dca1a0b6cdf8dbb46a (master)
Today I have checked this issue with following builds:
And we have run the attached project, It deployed and launched successfully on both Simulator and device.
Changing the status to Verified.
An Update to Comment#15
I have run this application for 15 minutes on physical device and I am not seeing any crash.