OS: Windows, does not need to be portable.
Background:
I've got subscribers and a publisher.
Subscriber.h
1 2 3 4 5 6 7 8
|
//Brief Subscriber details for simplicity
class Subscriber
{
//...
public:
virtual void Notify()=0;
//...
};
|
Publisher.h
1 2 3 4 5 6 7 8 9
|
//Brief publisher details for simplicity
class Publisher{
public:
void NotifySubscribers();
private:
SubscriberContainer m_subscribers; //Ignore container details
CriticalSectionWrapper m_criticalSectionProtector;
};
|
Now that that is out of the way. Here are my requirements. When I am Notifying Subscribers I cannot lock during the notification.
e.g. I cannot do this... (pseudo code)
1 2 3 4
|
void NotifySubscribers(){
Lock cslock(&m_criticalSectionProtector);
for_each(subscribers.Notify()); //Still holding lock during notify. Cannot do this
}
|
However, when I remove a subscriber I can hold a lock... e.g.
1 2 3 4
|
void RemoveSubscriber(Subscriber &subscriber){
Lock cslock(&m_criticalSectionProtector);
subscribers.remove(subscriber);
}
|
Somehow I need to be able to protect the subscribers, so that a remove does not occur during a notification, but during that notification not hold a lock. We are trying to avoid using boost::mutex, we want to use critical sections for it's efficiency (this may be a moot point).
I am trying to get to something like this in the NotifyingSubscribers.
1 2 3 4 5 6 7
|
void NotifySubscribers(){
Lock cslock(&m_criticalSectionProtector);
++IsBusyNotifying; //volatile int (maybe this could be a bool)
cslock.unlock(); //No longer holding the lock
for_each(subscribers.Notify()); //OK, no lock, but protected by the "flag"
--IsBusyNotifying;
}
|
Then in the remove... something like
1 2 3 4 5 6 7
|
void RemoveSubscriber(Subscriber &subscriber){
while(IsBusyNotifying)
Sleep(0);//Spin lock (maybe this is bad, another way?
//I think this is a problem see below for details (outside of code)
Lock cslock(&m_criticalSectionProtector);
subscribers.remove(subscriber);
}
|
Problem Details: If thread1 is has control and hits the while(IsBusyNotifying) and it is not busy, then thread2 takes over and hits the Lock inside Notifying subscribers, it will lock, set the flag, then unlock. Switch back to thread1, it's already passed the volatile variable and locks, but now the lock isn't protecting the container because there is no lock during notifications.
I hope what I've presented gives enough info for someone that has come across this problem before to remember a solution, or present a possible solution.
If you need anymore details do not hesitate to ask.
Summary:
Need a way to protect subscribers, but not hold a lock when notifying the subscribers. The solution need not be what I have presented.
Thanks.