How to protect update with critical section?

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.
Is this something that will require condition variables? Maybe I just will try using Boost and not do "premature optimization" until it is proven inefficient. Any suggestions otherwise?
Maybe a fresh set of eyes with their first cup of coffee can take a look at this this morning.
closed account (o1vk4iN6)
Does this need to be multi-threads ? It seems all functions in publisher (the ones listed, add, remove, notify - subscriber) will need a lock to function properly (I don't see how else you can do notify without a lock). If you are doing more then just this class in between threads then I suggest moving the lock outside of the class and request the lock whenever you need to use publisher. This is of course is me thinking this could also be used in non-threaded situations. I guess it really depends on how you are doing it.

1
2
3
4
5
6
7
8
class ThreadSafePublisher : public Publisher
{
public:
     virtual void NotifySubscribers() { /* lock here ... */ Publisher::NotifySubscribers(); }

private:
     CriticalSectionWrapper m_criticalSectionProtector;
};


All the functions mentioned will need to lock before accessing the container. Just suggesting some other means of doing so, although:

Another idea could be to have a thread-safe linked list, such that each "item" in the list has a lock. So when you are notifying subscribers you would "lock" the element of the list before accessing it's data. Do the same for adding / removing. You would need to be careful which items to lock when removing / adding depending on the implementation of the linked list. Of coarse it has it's pitfalls in that you need to lock for each element. So if you have 100 000 subscribers this might not be a good idea. How long does it really take one of these functions to run anyways. I don't suspect there would be a reason to go to these lengths; just add a lock to notifysubscribers.
Last edited on
Topic archived. No new replies allowed.