Since any of the handlers may cause an error, and this will prevent the rest of them from being called,
You say that is bad. This is a very good thing . When an unhandled, unexpected exception is thrown, it means that the whole process is now in an unknown, unpredictable, possibly dangerously unstable state.
Running more code at the moment is likely to make things worse, not better. The safest thing to do when this happens is to detect the situation and call failfast, which removes the entire process without running more code. You do not know what a terrible thing will be launched at this stage.
I want to ignore any errors coming from each handler.
This is a very dangerous idea. These exceptions tell you that something terrible is happening and you are ignoring them.
In other words, I donāt want an error in one handler to disrupt the execution of other handlers in the call list, because neither handlers or event publisher have any control over what any specific event handler code does.
Whose responsibility is here? Someone adds these event handlers to this event. This is the code that is responsible for ensuring that event handlers act correctly if there is an exception.
Then I create a lock object and lock it in the add and remove methods of an open event that change the private delegate variable called "event_handlers".
Of course, thatās good. I doubt the necessity of this function - I rarely have a situation where several threads add event handlers to an event - but I will take your word for it that you are in this situation.
But in this scenario, this code is very, very, very dangerous:
lock (collection_lock) foreach (Delegate handler in event_delegate.GetInvocationList()) try {handler.DynamicInvoke( args );}catch{}
Think about what's wrong there.
Thread Alpha enters collection lock.
Suppose there is another foo resource that is also controlled by another lock. Thread Beta enters foo lock to get some data that it needs.
Beta then takes this data and tries to lock the collection because it wants to use the contents of foo in the event handler.
Beta Thread is now waiting on the Alpha thread. Thread Alpha now calls a delegate who decides that he wants to access foo. So he waits in the stream of Beta, and now we have a dead end.
But can't we avoid this by ordering locks? No, because the premise of your script is that you do not know what event handlers do. If you already know that event handlers behave well in relation to blocking them, then you can probably also know that they behave well in order to not throw exceptions, and the whole problem disappears.
Ok, let's assume that you do this:
Delegate copy; lock (collection_lock) copy = event_delegate; foreach (Delegate handler in copy.GetInvocationList()) try {handler.DynamicInvoke( args );}catch{}
Delegates are immutable and atomically copied by reference, so now you know that you are going to reference the contents of event_delegate, but do not hold the lock during the call. Does it help?
Not really. You sold one problem for another:
Thread Alpha locks and makes a copy of the delegate list and leaves the lock.
Thread Beta takes the lock, removes the X event handler from the list, and destroys the state needed to prevent X from locking.
Thread Alpha grabs and launches X from the copy again. Because Beta simply destroyed the state necessary for X, X deadlocks to execute correctly. And again, you are at a standstill.
Event handlers should not do this; they must be strong in the face of their sudden becoming "stagnation." It looks like you are in a scenario in which you cannot trust event handlers to be well written. This is a terrible situation; then you cannot trust any code to be reliable in this process. It seems that you think that there is a certain level of isolation that you can impose on the event handler, catching all its errors and getting confused, but this does not happen. Event handlers are just code, and they can affect an arbitrary global state in a program, like any other code.
In short, your solution is generic, but it is not thread safe, and it is not error free. Rather, it exacerbates threading problems, such as interlocks, and disables security systems.
You simply cannot disclaim responsibility for event handlers being correct, so don't try. Write event handlers so that they are correct - so that they order locks correctly and never throw unhandled exceptions.
If they are wrong and ultimately throw exceptions, then clear this process immediately . Don't get confused trying to run code that is currently living in an unstable process.
Based on your comments on other answers, it seems you think you can get candy from strangers without any side effects. You cannot, not without unnecessary isolation. You cannot simply register a random code perforce for events in your process and hope for the best. If you have things that are unreliable because your application uses third-party code, you need a managed add-in structure to provide isolation. Try to find MEF or MAF.