예제 #1
0
            internal static void RemoveAllEventHandlers(Action <EventRegistrationToken> removeMethod)
            {
                object instanceKey = GetInstanceKey(removeMethod);

                System.Collections.Generic.Internal.List <EventRegistrationToken> tokensToRemove = new System.Collections.Generic.Internal.List <EventRegistrationToken>();

                //
                // 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
                {
                    EventCacheEntry registrationTokens = GetEventRegistrationTokenTableNoCreate(instanceKey, removeMethod);
                    if (registrationTokens == null)
                    {
                        // We have no information regarding this particular instance (IUnknown*/type) - just return
                        // This is necessary to avoid leaking empty dictionary/conditionalWeakTables for this instance
                        return;
                    }

                    try
                    {
                        registrationTokens.LockAcquire();

                        // Copy all tokens to tokensToRemove array which later we'll call removeMethod on
                        // outside this lock
                        foreach (KeyValuePair <object, EventRegistrationTokenListWithCount> item in registrationTokens.registrationTable)
                        {
                            item.Value.CopyTo(tokensToRemove);
                        }

                        // Clear the table - at this point all event handlers are no longer in the cache
                        // but they are not removed yet
                        registrationTokens.registrationTable.Clear();
#if false
                        BCLDebug.Log("INTEROP", "[WinRT_Eventing] Cache cleared for managed instance = " + instanceKey + "\n");
#endif
                    }
                    finally
                    {
                        registrationTokens.LockRelease();
                    }
                }
                finally
                {
                    s_eventCacheRWLock.ExitReadLock();
                }

                //
                // Remove all handlers outside the lock
                //
#if false
                BCLDebug.Log("INTEROP", "[WinRT_Eventing] Start removing all events for instance = " + instanceKey + "\n");
#endif
                CallRemoveMethods(removeMethod, tokensToRemove);
#if false
                BCLDebug.Log("INTEROP", "[WinRT_Eventing] Finished removing all events for instance = " + instanceKey + "\n");
#endif
            }
예제 #2
0
            internal static void RemoveEventHandler <T>(Action <EventRegistrationToken> removeMethod, T handler)
            {
                object instanceKey = GetInstanceKey(removeMethod);

                EventRegistrationToken token;

                //
                // 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
                {
                    EventCacheEntry registrationTokens = GetEventRegistrationTokenTableNoCreate(instanceKey, removeMethod);
                    if (registrationTokens == null)
                    {
                        // We have no information regarding this particular instance (IUnknown*/type) - just return
                        // This is necessary to avoid leaking empty dictionary/conditionalWeakTables for this instance
#if false
                        BCLDebug.Log("INTEROP", "[WinRT_Eventing] no registrationTokens found for instance=" + instanceKey + ", handler= " + handler + "\n");
#endif
                        return;
                    }

                    try
                    {
                        registrationTokens.LockAcquire();

                        EventRegistrationTokenListWithCount tokens;

                        // Note:
                        // When unsubscribing events, we allow subscribing the event using a different delegate
                        // (but with the same object/method), so we need to find the first delegate that matches
                        // and unsubscribe it
                        // It actually doesn't matter which delegate - as long as it matches
                        // Note that inside TryGetValueWithValueEquality we assumes that any delegate
                        // with the same value equality would have the same hash code
                        object key = FindEquivalentKeyUnsafe(registrationTokens.registrationTable, handler, out tokens);

                        Debug.Assert((key != null && tokens != null) || (key == null && tokens == null),
                                     "key and tokens must be both null or non-null");
                        if (tokens == null)
                        {
                            // Failure to find a registration for a token is not an error - it's simply a no-op.
#if false
                            BCLDebug.Log("INTEROP", "[WinRT_Eventing] no token list found for instance=" + instanceKey + ", handler= " + handler + "\n");
#endif
                            return;
                        }

                        // Select a registration token to unregister
                        // Note that we need to always get the last token just in case another COM object
                        // is created at the same address before the entry for the old one goes away.
                        // See comments above s_eventRegistrations for more details
                        bool moreItems = tokens.Pop(out token);

                        // If the last token is removed from token list, we need to remove it from the cache
                        // otherwise FindEquivalentKeyUnsafe may found this empty token list even though there could be other
                        // equivalent keys in there with non-0 token list
                        if (!moreItems)
                        {
                            // Remove it from (handler)->(tokens)
                            // NOTE: We should not check whether registrationTokens has 0 entries and remove it from the cache
                            // (just like managed event implementation), because this might race with the finalizer of
                            // EventRegistrationTokenList
                            registrationTokens.registrationTable.Remove(key);
                        }
#if false
                        BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event unsubscribed for managed instance = " + instanceKey + ", handler = " + handler + ", token = " + token.m_value + "\n");
#endif
                    }
                    finally
                    {
                        registrationTokens.LockRelease();
                    }
                }
                finally
                {
                    s_eventCacheRWLock.ExitReadLock();
                }
                // Call removeMethod outside of RW lock
                // At this point we don't need to worry about race conditions and we can avoid deadlocks
                // if removeMethod waits on finalizer thread
                removeMethod(token);
            }
예제 #3
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;
                }
            }