I am writing a program (for homework) that simulates a unisex bathroom. Only 4 people are allowed at a time and men and woman cannot enter if the other sex is already using the bathroom. My problem is with allowing a max of 4 people in the bathroom. As you can see from the output, only 1 person is getting into the restroom at a time. Here is my code:
const int Delayx = 60;
int i;
int restroom = 0;
int Menwaiting = 0;
int Womenwaiting = 0;
semaphore max_capacity;
semaphore woman;
semaphore man;
semaphore mutex;
semaphore restroomcount;
void Delay(void)
{
int DelayTime;
DelayTime = random(Delayx);
for (i = 0; i<DelayTime; i++);
}
void Woman(void)
{
// for(;;){
Womenwaiting++;
//wait(mutex);
wait(woman);
wait(max_capacity);
//wait(woman);
wait(mutex);
wait(restroomcount);
cout << "A Woman has entered Restroom"<<endl;
cout << "People in the Restroom:" << restroom++ <<endl <<endl;
signal(restroomcount);
Womenwaiting--;
Delay();
wait(restroomcount);
cout << "A woman has exited Restroom"<<endl;
cout << "People in the Restroom:" << restroom-- <<endl<<endl;
signal(restroomcount);
signal(mutex);
signal(max_capacity);
if(Menwaiting > Womenwaiting){
signal(man);
}
else{
signal(woman);
}
//signal(max_capacity);
//signal(man);
// }
}
void Man(void)
{
// for(;;){
Menwaiting++;
//wait(mutex);
wait(man);
wait(max_capacity);
//wait(man);
wait(mutex);
wait(restroomcount);
cout <<"A Man has entered the Restroom"<<endl;
cout <<"People in the Restroom:" << restroom++ <<endl<<endl;
signal(restroomcount);
Menwaiting--;
//signal(mutex);
Delay();
//wait(mutex);
wait(restroomcount);
cout << "A man has exited the Restroom"<<endl;
cout <<"People in the Restroom:" << restroom-- <<endl<<endl;
signal(restroomcount);
signal(mutex);
signal(max_capacity);
if(Womenwaiting > Menwaiting){
signal(woman);
}
else{
signal(man);
}
//signal(max_capacity);
//signal(woman);
//}
}
void main()
{
initialsem(woman,1);
initialsem(man,1);
initialsem(max_capacity,4);
initialsem(mutex,1);
initialsem(restroomcount,1);
cobegin
{
Woman(); Woman(); Woman(); Woman(); Woman(); Man(); Man(); Man(); Man(); Man();
}
}
This generates the following output:
A Man has entered the Restroom
People in the Restroom:1A man has exited the Restroom
People in the Restroom:0A Man has entered the Restroom
People in the Restroom:1A man has exited the Restroom
People in the Restroom:0A Woman has entered Restroom
People in the Restroom:1A woman has exited Restroom
People in the Restroom:0A Woman has entered Restroom
People in the Restroom:1A woman has exited Restroom
People in the Restroom:0
And so on, forever.
is the use of semaphores a requirement? for example, in "c++" pseudo-code, a implementation would look like:
First lets create a state object and a function that validates transitions between states
Lets also create a global reference to the current state and a function to update the current state based on some next desired state
then we create a global vector to hold the states, and a function representing a women thread trying to go to the bathroom
and the main function with process loop logic and initialization
this code should work ( i haven't tested it ). I've cheated a bit because i'm not deleting any of the provisional states created, so each state persist until the process dies. proper garbage collection would require a technique called hazard pointer management.
Note that i dont use any mutex semaphores or lock, the only locking primitive i am using is the CAS( address, old_value , new_value ) (compare and swap). This primitive atomically compares a pointer (address) and if it still contains (old_value) then it assign it new_value and succeeds, otherwise it fails. Also, you still need a global lock for the std::vector storing the states that i have not included in the code (you can also just leak them, but i store them somewhere so you can think that those should be deleted once you know how GC could be made to work in these cases)
Since all my intermediate states are inmutable (lisp/clojure style inmutabilitity) the contention (and hence, starvation) of the threads vastly improves. In your example the set of states is small (just a bunch of persons) its not too bad that we don't delete the used states.
however, even with the problems i've mentioned, i think you would agree that the logic of what is happening is much more explicit and readable.
I think you have too many semaphores. Your man/woman semaphores are gating to 1 person at a time. Consider using some state variables protected by mutexes (current sex of bathroom, number of people in bathroom) rather than so many different semaphores.
Do you maintain a line ordering or can people skip based on the current restroom sex? For instance, if you have woman,woman,woman,man,woman, is the 4th woman allowed to skip the man and go into the restroom, or do the 3 women exit, then the man enters/exits, then the woman can enter? This is an easier problem than allowing a skip.
Issues with the question
The original code isn't very OO.
The processing of the bathroom queue should be seperate from the generation of the people in the queue - if not running a seperate thread at least after the queue is filled.
Making the assumption that there are basically separate queues of men and women - not intermixed in some fixed order, otherwise the problem doesn't make any sense to use a semaphore.
The problem doesn't describe how many people get to enter when the condition is right, male toilet with more men, do you fill it to 4 or only until the queue of men is less than women again?
Even so the problem as described (and based on sample code with no threading) doesn't work well with a semaphore in my opinion, the main problem is that the semaphore doesn't yield the count easily and a successful wait changes the count.
The interesting thing I see in the problem is the inefficiency in a near equal queue length and trading between disallowing another of the same sex into the toilet and the chance that before the remaining persons in the toilet leave the number of the same sex becomes larger again. Lets face it, it's unisex and so it should allow 4 people in regardless of gender ;)
Proposed solution
So you need to use a semaphore, the interesting things about a semaphore is the recording of multiple uses (unlike mutex) and if there is not free space then it will possibly wait. It does not discriminate however between those waiting, it will only tell that there is space free.
Have 1 semaphore and think you should check the semaphore when a person enters the queue or when somebody leaves the bathroom.
You could then have 1 'queue' each for men and women (from given this is basically a count). These queues are not really related or limiting on each other in terms of entry and so have nothing to do with semaphores. Each could follow a locking free provider pattern, but you might find it easier to use a mutex to synchronise so that you can examine the size of the queues and manipulate them. In the following I've just used the count directly, instead it should be using some form of InterlockedIncrement and InterlockedDecrement to protect against adding and removing people from the same queue.
In the rough, Bathroom.h
Bathroom.cpp
Program.cpp
Edit 5 (I realize this may be too little too late, as this was likely a homework assignment, but I just thought of a way to do this using only semaphores.)
okay here's my pseudocode:
The "Man" thread should behave similarly. Keep in mind that waiting and mutex semaphores protect both men and women variables.
You are signalling the mutex before a man/woman has "exited" the bathroom. If I understand correctly, the mutex is so that only one sex is in the bathroom at any time. Since you are signalling it before outputting "A Man has exited the bathroom", a woman can get the mutex and get in.
Since men and women are initially waiting on two different semaphores It's understandable that some will get in to that initial semaphore. From there it appears you are getting the mutex (shared between men and women) then after getting in you release it before they exit. Perhaps you mean to be signalling the "man" or "woman" semaphore here instead?
Edit: I guess the gist of my answer is this: The mutex is shared between men and women. In your code, when a person gets the mutex they say they're in, you decrement that they're waiting, then you release the mutex. Think about that last step a little more deeply. If you're releasing the mutex before they've left, what's possible here?
Edit2 (in response to your comments): What does your new code look like (Edit your original post)?
This will help us abstract the code away to the logic and then we can try to structure your logic properly and compare what we think is right to what your code is doing.
Edit3: Okay, it looks like you're getting closer. Here's some things to think about (I'm not posting a complete solution because this is homework and I want you to learn!)
Think especially hard about the restroom count semaphore... (HINT: It may be more important than simply protecting the count variable. It may need to be protecting the releasing of other semaphores...)
Edit 4: So I finally realized that you are trying to avoid starvation in this problem (as indicated by comments below). Though your homework is very similar to the readers/writers problem, the additional constraint to avoid starvation by either thread type makes it difficult to implement. I personally do not know how to do this without using Events to set preference (http://msdn.microsoft.com/en-us/library/dd492846.aspx), and even then there is no guarantee that starvation could never happen, which if I understand the Wikipedia (http://en.wikipedia.org/wiki/Readers-writers_problem#The_third_readers-writers_problem) article on this topic properly, is the only way to do it.
Are you allowed to use events?
I must apologize for not completely grokking this additional readers/writers constraint of no starvation. Hopefully somebody else can help you better.