Implementing a stock exchange using monitors - concurrency

I am trying to implement a stock exchange using Hoare's monitors.
It has two functions buy() and sell() as follow:
buy(procid, ticker, nshares, limit)
sell(procid, ticker, nshares, limit)
And should print information on buyer id, seller id, ticker, number of shares, and price.
And fairness is always satisfied.
The pseudo-code of my solution is as follows, but it's not complete.
It basically uses a condition variable queue for each ticker. A seller process is put to sleep on this queue when it sends a sell order to the stock exchange, and a buyer process signals this seller process that it wants to buy if the conditions (matching price limit and number of shares) are satisfied.
monitor SE {
int available_shares;
int price;
sell(procid, ticker, nshares, limit) {
wait(ticker); // put sell order in ticker queue
available_shares += nshares;
price = limit;
printf("Starting transaction with seller %i", procid);
}
buy(procid, ticker, nshares, limit) {
if (limit <= price && nshares <= available_shares) {
signal(ticker);
available_share -= nshares;
printf("Completing transaction with buyer %i", procid);
printf("Transacting %i %s shares at %i", nshares, ticker, limit);
} else {
wait(ticker); // put buy order in ticker queue
}
}
}
Would such an approach be able to handle multiple buy and sell orders for multiple tickers? Or does it lead to a dead-end?

To solve the deadlock problem I would use two condition variables one for buyers and one for sellers. Each method first modifies available_shares, then signals its own condition variable and finally waits on the other condition variable. Even though, each operation has to recheck the condition about available_shares after it wakes up to complete the transaction or to go to sleep again.
The problem here is that this does not keep track on how much you are buying/selling from/to who. It does not even guarantee that the seller sells all its shares in a transaction. So, in response to your original question I don't see how such an approach would be able to handle multiple buy and sell orders for multiple tickers. I propose this other solution which use a HashTable or dictionary in which each key is a limit and each value is a priority queue or a sorted list ordered by the tickers:
monitor SE {
int available_shares;
int price;
Dictionary<int, SortedList<int, Transac>> Ts;
sell(procid, ticker, nshares, limit) {
Transac t = new Transac(procid, nshares, limit);
Ts[limit].enqueue(ticker, t); //probably you should verify first if the entry is not null
available_shares += nshares;
notifyAll(tickerB);
while(Ts[limit][ticker] > 0)
wait(tickerS);
printf("Starting transaction with seller %i", Ts[limit][ticker].procid);
}
buy(procid, ticker, nshares, limit) {
int nshares_copy = nshares;
while(true){
int cnt = 0;
List<Transac> tmp = new List<Transac>();
for(int i = 0; i < Ts.keys.length && cnt < nshares; i++){
if(Ts.keys[i] <= limit){
for(int j = 0; j < Ts[Ts.keys[i]].lenght && cnt < nshares; j++){
cnt += Ts[Ts.keys[i]][j].nshares;
tmp.add(Ts[Ts.keys[i]][j]);
}
}
}
if(nshares <= cnt){
available_share -= nshares;
foreach(Transac t in tmp){
int min = min(t.nshares, nshares);
t.nshares -= min;
nshares -= min;
}
break;
} else {
wait(tickerB);
}
}
notifyAll(tickerS);
printf("Completing transaction with buyer %i", procid);
printf("Transacting %i %s shares at %i", nshares_copy, ticker, limit);
}
}
I did this using monitors to follow your initial idea, but I have to say that I don't think this is the best way. I think a more fine-grain lock could give you a better performance (such as locks or atomic operations).
Note: The code has not been tested. So, I might have left out some implementation details

Related

Array returns bad values only first time

So I'm working with Steamworks (leaderboards) and i have some strange issue. When i fire my function to get scores, from debugging i know that it works just fine.However my array after 1st function run always returns default values.After I fire function for the second time everything works perfectly fine. I tried to track down the issue however i failed.
Here is my whole code that i am using in this case:
Struct for stats
USTRUCT(BlueprintType)
struct FScorePackage
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Leaderboard")
FString PlayerName = "working";
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Leaderboard")
int32 Rank = 0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Leaderboard")
int32 Score = 0;
};
Function that sent request to the steam:
.h
UFUNCTION(BlueprintCallable, Category = "Steam|Leaderboard", meta = (Latent, LatentInfo = "LatentInfo", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
TArray<FScorePackage> DownloadScoresAroundUser(UObject* WorldContextObject, int AboveUser, int BelowUser, struct FLatentActionInfo LatentInfo);
.cpp
TArray<FScorePackage> USteamLeaderboard::DownloadScoresAroundUser(UObject* WorldContextObject, int AboveUser, int BelowUser, struct FLatentActionInfo LatentInfo)
{
if (!m_CurrentLeaderboard)
{
return Scores;
}
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject))
{
FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
if (LatentActionManager.FindExistingAction<SteamLeaderboardLatentClass>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
{
// load the specified leaderboard data around the current user
SteamAPICall_t hSteamAPICall = SteamUserStats()->DownloadLeaderboardEntries(m_CurrentLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -AboveUser, BelowUser);
m_callResultDownloadScore.Set(hSteamAPICall, this,&USteamLeaderboard::OnDownloadScore);
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new SteamLeaderboardLatentClassScores(LatentInfo));
return Scores;
}
return Scores;
}
return Scores;
}
Now callback function from steam:
.h
void OnDownloadScore(LeaderboardScoresDownloaded_t *pResult, bool bIOFailure);
CCallResult <USteamLeaderboard, LeaderboardScoresDownloaded_t> m_callResultDownloadScore;
.cpp
void USteamLeaderboard::OnDownloadScore(LeaderboardScoresDownloaded_t *pCallback, bool bIOFailure)
{
if (!bIOFailure)
{
m_nLeaderboardEntries = __min(pCallback->m_cEntryCount, 30);
for (int index = 0; index < m_nLeaderboardEntries; index++)
{
SteamUserStats()->GetDownloadedLeaderboardEntry(pCallback->m_hSteamLeaderboardEntries, index, &m_leaderboardEntries[index], NULL, 0);
}
TranslateEntries();
scores = true;
}
}
And finally function that write scores in Array:
.h
UFUNCTION(BlueprintCosmetic, Category = "Steam|Leaderboard")
TArray<FScorePackage> TranslateEntries();
.cpp
TArray<FScorePackage> USteamLeaderboard::TranslateEntries()
{
FScorePackage ThisScore;
Scores.Init(ThisScore, 30);
for (int i = 0; i < 30; i++)
{
ThisScore.PlayerName = GetSteamName(m_leaderboardEntries[i].m_steamIDUser);
ThisScore.Rank = m_leaderboardEntries[i].m_nGlobalRank;
ThisScore.Score = m_leaderboardEntries[i].m_nScore;
Arrayas[i] = ThisScore;
}
return Scores;
}
Scores array is just static TArray Scores and scores=true is only for latent check to go on with functions after calling DownloadScoresAroundUser :)
My normal flow with this is:
1.I already have handle for leaderboard.
2.I'm calling DownloadScoresAroundUser.
3.Flow goes to latent which cannot proceed becouse of scores=false.
4.After i got callback from steam OnDownloadScore fires, giving me all needed info(checked if really and it does!).
5.Then i call TranslateEntries to get all scores with names and rank in Array.
6.Then I'm printing whole array (with break package in unreal) and get default values of my struct.
7.After i fire whole cycle again i get proper values.
If any further info is required let me know :)
This is a bit of a guess, but it appears that you have a latency issue. When you make the request to download the scores, this is a time consuming call that does not block. You set up a callback that will be called when the scores are ready, then return the existing empty Scores object.
When you make your second call, enough time has passed for the scores to have download and Scores to be populated, so it returns some scores.
Note that you have a potential race condition, where DownloadScoresAroundUser can access (return) Scores while your callback is populating that vector.
Here's one possible solution. Before the scores have completed loading, DownloadScoresAroundUser returns an empty Score (or possibly one indicating that scores are being loaded). Once the scores have been loaded and Scores populated, it will return those. Also, the callback (besides populating Scores) can in some fashion notify the caller(s) of DownloadScoresAndUser that new scores are available. They can respond to that by calling in again to get the updated scores and refresh the display.
Translateentries copy data from 0 to 30 but only "Callback->m_cEntryCount" are actually initialized. So if it < at 30, the data from "Callback->m_cEntryCount" to 30 may be wrong. Can you print out the value of this variable "in SteamLeaderboard::OnDownloadScore" ?

Count of CheckBoxList items selected isn't being calculated correctly

I'm currently trying to get a count of a CheckBoxList to use in conjunction with an if and else statement for a project. If the count is not equal to 2 I have a label that prompts the user to select two modules but the count isn't being calculated properly. Any help would be appreciated. Here is the code in question:
protected void RegisterButton_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["UniString"].ConnectionString);
//This integer will hold the number of modules selected
Int32 amount = 0;
for (int i = 0; i < CheckBoxList1.Items.Count; i++)
{
//amount will increment each time a module checkbox is checked
if (CheckBoxList1.Items[i].Selected)
{
amount = amount++;
}
//If amount is not equal to 2 the code below will run
if (amount != 2)
{
//If the number of modules selected is not equal to 2 then this message is displayed and the background of the label in the markup is turned red to draw attention
ModuleSelectionMessage.Text = "Please select 2 modules to successfully register";
ModuleSelectionMessage.BackColor = System.Drawing.Color.Red;
}
else
{...
amount = amount++ is not assign amount value properly,
use amount++ or amount= amount + 1 instead of that..
for (int i = 0; i < CheckBoxList1.Items.Count; i++)
{
//amount will increment each time a module checkbox is checked
if (CheckBoxList1.Items[i].Selected)
{
amount = amount + 1;
}
//other code...
}

Value of an object will not be changed

I created a method which should change values in my shop object. Unfortunately the values are not changed.
edit: 18:13
I could create a new shop object and return it, but I thought it should work with passing the object by reference?
To make my question clearer: My only problem is, that the new values are not stored in the object. I did run the debugging and the values are all correctly calculated and as expected.
The problem is in the lines:
shop.get_stock().push_back(inventory_bonbon);
This line should push a new inventory item to the vector (containing the inventory items), if this inventory item is currently not in stock.
Here I increase the amount of an inventory item, when the item is currently in stock:
i_inventory_shop.increase_amount(shop.get_stock()[i], amount);
(I have unit-tested the increase_amount() method and it works fine.)
The two lines are called as expected (meaning I find when an item is in stock or not).
void IRooms::increase_inventory_shop(Shop & shop, Bonbon & bonbon, int amount)
{
OutputDebugString("I_Game Logic increase_inventory_shop called \n");
IInventoryItemsBonbons i_inventory_shop;
bool bonbon_in_shop = false;
for (int i = 0; i < shop.get_stock().size(); i++)
{
OutputDebugString(("I_Game Logic shop vector size \n" + std::to_string(shop.get_stock().size()) + "\n").c_str());
OutputDebugString(("I_Game Logic bonbon name \n" + bonbon.get_name() + "\n").c_str());
OutputDebugString(("I_Game Logic bonbon amount \n" + std::to_string(amount) + "\n").c_str());
if (bonbon.get_name() == shop.get_stock()[i].get_bonbon().get_name())
{
bonbon_in_shop = true;
OutputDebugString("Bonbon found \n");
i_inventory_shop.increase_amount(shop.get_stock()[i], amount);
break;
}
}
if (bonbon_in_shop == false) {
OutputDebugString("Bonbon not found \n");
InventoryItemBonbons inventory_bonbon = i_inventory_shop.create(amount, bonbon);
shop.get_stock().push_back(inventory_bonbon);
}
}
This method calls: (the method below, I have tested it)
void IInventoryItemsBonbons::increase_amount(InventoryItemBonbons & inventoryitem_shop, int amount)
{
int old_amount = inventoryitem_shop.get_amount();
int new_amount = old_amount + amount;
inventoryitem_shop.set_amount(new_amount);
}
edit 17:51:
Shop.h
std::vector<InventoryItemBonbons> get_stock();
Shop.ccp
std::vector<InventoryItemBonbons> Shop::get_stock()
{
return stock_bonbons;
}
_____________________________________________________________________________edit: 19:54
I have now introduced local variables and I return the local shop.
Shop IRooms::increase_inventory_shop(Shop & shop, Bonbon & bonbon, int amount)
{
Shop shop_temp = shop;
std::vector<InventoryItemBonbons> inventory_items_temp = shop.get_stock();
IInventoryItemsBonbons i_inventory_shop;
bool bonbon_in_shop = false;
for (int i = 0; i < shop_temp.get_stock().size(); i++)
{
if (bonbon.get_name() == shop_temp.get_stock()[i].get_bonbon().get_name())
{
bonbon_in_shop = true;
i_inventory_shop.increase_amount(inventory_items_temp[i], amount);
break;
}
}
if (bonbon_in_shop == false) {
InventoryItemBonbons inventory_bonbon = i_inventory_shop.create(amount, bonbon);
inventory_items_temp.push_back(inventory_bonbon);
}
shop_temp.set_stock(inventory_items_temp);
//shop = shop_temp;
//return shop;
return shop_temp;
}
The only thing I want to know, why the values of shop won't change. I have tried to copy shop_temp to shop, but even this does not work.
std::vector<InventoryItemBonbons> get_stock();
Since get_stock returns by value, not by reference, any changes to the value returned will be lost as soon as that temporary goes out of scope.
shop.get_stock().push_back(inventory_bonbon);
So this modifies the temporary returned by get_stock, which immediately goes out of scope, is destroyed, and the modification is lost.
You probably wanted:
std::vector<InventoryItemBonbons>& get_stock();
...
std::vector<InventoryItemBonbons>& Shop::get_stock()
{
return stock_bonbons;
}

how to write an atomic account transfer function

So suppose I get two bank account A and B, and I need to atomically transfer money.
The set up is the following:
`
struct account{
int64 amount;
pthread_mutex_lock m;
}
`
here is my approach:
`
bool Transfer(int from_account, int to_account, int64 amount)
{
pthread_lock(&account[from_account].m);
bool ret = false;
if(accounts[from_account].balance>=amount)
{
accounts[from_account].balance-=amount;
ret = true;
}
pthread_unlock(&account[from_account].m);
pthread_lock(&account[to_account].m);
accounts[to_account].balance+=amount;
pthread_unlock(&account[to_account].m);
return ret;
}
`
the function transfer money from from_account to to_account, return a bool, only transfer when remaining money in the account>=ammount.
Is this function an okay approach? I guess it will not cause deadlock problem but doesn't it make the whole function non-atomic? So there might be race conditions? Please help me, thanks a lot.
Your code is logically broken. Regardless of the from_account's balance, to_account will win account unconditionally! (and I wanna be to_account owner :)
In this case, you must acquire both locks of two accounts, and it cause potentially deadlock problem.
The simplest way to avoid deadlock is enforcing the order of lock acquisition, for example, the smaller index account comes first.
bool Transfer(int from_account, int to_account, int64 amount)
{
// acquire locks (in pre-defined order)
if (from_account < to_account)
{
pthread_lock(&accounts[from_account].m);
pthread_lock(&accounts[to_account].m);
} else {
pthread_lock(&accounts[to_account].m);
pthread_lock(&accounts[from_account].m);
}
// transfer amount
bool ret = false;
if (accounts[from_account].balance >= amount)
{
accounts[from_account].balance -= amount;
accounts[to_account].balance += amount;
ret = true;
}
// release both locks
pthread_unlock(&accounts[from_account].m);
pthread_unlock(&accounts[to_account].m);
return ret;
}

Mutual exclusion and semaphores

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:1
A man has exited the Restroom
People in the Restroom:0
A Man has entered the Restroom
People in the Restroom:1
A man has exited the Restroom
People in the Restroom:0
A Woman has entered Restroom
People in the Restroom:1
A woman has exited Restroom
People in the Restroom:0
A Woman has entered Restroom
People in the Restroom:1
A woman has exited Restroom
People in the Restroom:0
And so on, forever.
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.
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
struct BathRoomState
{
int women;
int men;
BathRoomState( int w , int m ) : women(w) , men(m) {}
bool hasWomen()
{
if (women > 0 && men == 0)
return true;
return false;
}
bool isEmpty()
{
return (women + men == 0);
}
static bool isValidTransition( BathRoomState* a , BathRoomState* b )
{
if (a->HasWomen())
{
if ( (abs( a->women - b->women ) == 1) && (a->men == b->men) )
return true;
else false;
} else if (a->isEmpty())
{
if ((b->women == 1 && b->men == 0)
|| (b->women == 0 && b->men == 1))
return true else false;
} else //a has men
{
if ((abs( a->men - b->men ) == 1) && ( a->women == b->women))
return true else false;
}
}
}
Lets also create a global reference to the current state and a function to update the current state based on some next desired state
BathRoomState* currentBathroomState = 0;
bool TryToChangeState(BathRoomState* newState)
{
BathRoomState* existingState = currentBathroomState;
if (BathRoomState::isValidTransition( existingState , newState ))
{
//this atomic operation depends on library support
bool success = CompareAndSwapAtomically( currentBathroomState , existingState , newState );
return success;
}
}
then we create a global vector to hold the states, and a function representing a women thread trying to go to the bathroom
std::vector< BathRoomState* > noGCinThisExample;
//thread functtion
void women()
{
BathRoomState* existingState = currentBathroomState;
BathRoomState* newState = new BathRoomState( existingState.women+1 , existingState.men );
while (!TryToChangeState(newState))
{
//yield or sleep from time to time here to let other threads progress
existingState = currentBathroomState;
newState.women = existingState.women + 1;
newState.men = existingState.men;
}
noGCinThisExample.push_back( newState ); //no GC in this example
//the woman is in the bathroom now. lets give her some time
delayForWomen();
//lets try to get her out
BathRoomState* exitState = new BathRoomState( existingState.women-1 , existingState.men );
while (!TryToChangeState(exitState ))
{
//yield or sleep from time to time here to let other threads progress
existingState = currentBathroomState;
exitState.women = existingState.women - 1;
exitState.men = existingState.men;
}
noGCinThisExample.push_back( exitState); //no GC in this example
}
//homework: do a similar function for men
and the main function with process loop logic and initialization
void main()
{
BathRoomState* initialState = new BathRoomState( 0 , 0);
noGCinThisExample.push_back( initialState );
currentBathroomState = initialState;
while(some_condition)
{
if (random() > 0.5)
thread( women() );
else
thread( men() );
}
};
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.
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
class Bathroom
{
public:
Bathroom(void);
~Bathroom(void);
AddMan();
AddWoman();
Run();
private:
StateChange();
int m_Menwaiting;
int m_Womenwaiting;
semaphore max_capacity;
enum Users {
NOBODY ,
WOMEN,
MEN
} m_inUseBy;
};
Bathroom.cpp
Bathroom::Bathroom(void)
: m_Menwaiting(0)
, m_Womenwaiting(0)
, m_inUseBy(NOBODY)
{
initialsem(max_capacity,4);
}
Bathroom::~Bathroom(void)
{
freesem(max_capacity);
}
Bathroom::AddMan(){
++m_Menwaiting;
StateChange();
}
Bathroom::AddWoman(){
++m_Womenwaiting;
StateChange();
}
Bathroom::StateChange() {
// extra at a time
if( m_Menwaiting > m_Womenwaiting && inUseBy != WOMEN ) {
if( wait(max_capacity,0 delay) != timeout )
m_Menwaiting--;
}
if( m_Womenwaiting > m_Menwaiting && inUseBy != MEN ) {
if( wait(max_capacity,0 delay) != timeout )
m_Womenwaiting--;
}
// all available slots
if( m_Menwaiting > m_Womenwaiting && inUseBy != WOMEN ) {
while( wait(max_capacity,0 delay) != timeout )
m_Menwaiting--;
}
if( m_Womenwaiting > m_Menwaiting && inUseBy != MEN ) {
while( wait(max_capacity,0 delay) != timeout )
m_Womenwaiting--;
}
}
Bathroom::run(){
// people leaving bathroom simulated
while(1) {
Delay();
signal(max_capacity);
StateChange();
}
}
Program.cpp
Bathroom b1;
addPeople() {
while(true) {
// randomly add people
Delay();
b1.AddMen();
b1.AddWomen();
}
}
int main(){
thread( addPeople );
b1.run();
}