Esempio n. 1
0
            internal static void AddEventHandler <T>(Func <T, EventRegistrationToken> addMethod,
                                                     Action <EventRegistrationToken> removeMethod,
                                                     T handler)
            {
                // The instanceKey will be IUnknown * of the target object
                object instanceKey = GetInstanceKey(removeMethod);

                // Call addMethod outside of RW lock
                // At this point we don't need to worry about race conditions and we can avoid deadlocks
                // if addMethod waits on finalizer thread
                // If we later throw we need to remove the method
                EventRegistrationToken token = addMethod(handler);

                bool tokenAdded = false;

                try
                {
                    EventRegistrationTokenListWithCount tokens;

                    //
                    // The whole add/remove code has to be protected by a reader/writer lock
                    // Add/Remove cannot run at the same time with cache cleanup but Add/Remove can run at the same time
                    //
                    s_eventCacheRWLock.EnterReadLock();
                    try
                    {
                        // Add the method, and make a note of the delegate -> token mapping.
                        EventCacheEntry registrationTokens = GetOrCreateEventRegistrationTokenTable(instanceKey, removeMethod);

                        try
                        {
                            registrationTokens.LockAcquire();

                            //
                            // We need to find the key that equals to this handler
                            // Suppose we have 3 handlers A, B, C that are equal (refer to the same object and method),
                            // the first handler (let's say A) will be used as the key and holds all the tokens.
                            // We don't need to hold onto B and C, because the COM object itself will keep them alive,
                            // and they won't die anyway unless the COM object dies or they get unsubscribed.
                            // It may appear that it is fine to hold A, B, C, and add them and their corresponding tokens
                            // into registrationTokens table. However, this is very dangerous, because this COM object
                            // may die, but A, B, C might not get collected yet, and another COM object comes into life
                            // with the same IUnknown address, and we subscribe event B. In this case, the right token
                            // will be added into B's token list, but once we unsubscribe B, we might end up removing
                            // the last token in C, and that may lead to crash.
                            //
                            object key = FindEquivalentKeyUnsafe(registrationTokens.registrationTable, handler, out tokens);

                            if (key == null)
                            {
                                tokens = new EventRegistrationTokenListWithCount(registrationTokens.tokenListCount, token);
                                registrationTokens.registrationTable.Add(handler, tokens);
                            }
                            else
                            {
                                tokens.Push(token);
                            }

                            tokenAdded = true;
                        }
                        finally
                        {
                            registrationTokens.LockRelease();
                        }
                    }
                    finally
                    {
                        s_eventCacheRWLock.ExitReadLock();
                    }
#if false
                    BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event subscribed for instance = " + instanceKey + ", handler = " + handler + "\n");
#endif
                }
                catch (Exception)
                {
                    // If we've already added the token and go there, we don't need to "UNDO" anything
                    if (!tokenAdded)
                    {
                        // Otherwise, "Undo" addMethod if any exception occurs
                        // There is no need to cleanup our data structure as we haven't added the token yet
                        removeMethod(token);
                    }


                    throw;
                }
            }
Esempio n. 2
0
 private static object FindEquivalentKeyUnsafe(ConditionalWeakTable <object, EventRegistrationTokenListWithCount> registrationTable, object handler, out EventRegistrationTokenListWithCount tokens)
 {
     foreach (KeyValuePair <object, EventRegistrationTokenListWithCount> item in registrationTable)
     {
         if (Object.Equals(item.Key, handler))
         {
             tokens = item.Value;
             return(item.Key);
         }
     }
     tokens = null;
     return(null);
 }