Bugzilla – Bug 12212
iPhoneOSGameView.Run throws "An element with the same key already exists in the dictionary."
Last modified: 2013-06-14 04:41:57 EDT
Rarely, I get this error when calling Run:
System.ArgumentException: An element with the same key already exists in the
(ContextHandle key, System.WeakReference value) [0x00000] in <filename
at OpenTK.Graphics.GraphicsContext..ctor (OpenTK.Graphics.GraphicsMode
mode, IWindowInfo window, Int32 major, Int32 minor, GraphicsContextFlags flags)
[0x00000] in <filename unknown>:0
(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
at WAML.IOS.RenderBoxImplementation.Start () [0x00000] in <filename
I haven't been able to repro it, but it's happened quite a few times. It seems
to be random. Saw in mtouch 184.108.40.206 (8d98f5e).
Googling it showed someone else that ran into this problem too, but his post to
the forums did not receive any replies:
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
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
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
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
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
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
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
Fixes for the two issues are now fixed in
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.