FRI-OS
example/posix/threads-bingo_detached.cpp

Simulation of the game Bingo! using wrapped posix threads

//LDFLAGS = -lpthread
#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <frios/posix.hpp>
// custom implementation of an atomic integer value
class atomic_int
{
private:
// the integer value
int _value;
// the mutex for synchronization of access to _value
public:
atomic_int(int initial = 0)
: _value(initial)
{ }
// interlocked conversion to int
operator int (void)
{
return _value;
}
// interlocked assignment
atomic_int& operator = (int value)
{
_value = value;
return *this;
}
// interlocked prefix increment
atomic_int& operator ++(void)
{
++_value;
return *this;
}
// atomically set the new value if current value is zero
void set_if_zero(int value)
{
if(!_value) _value = value;
}
};
// custom implementation of semaphore using mutex and condition variable
class semaphore
{
private:
int _value;
public:
// initialize the semaphore
semaphore(int initial = 0)
: _value(initial)
{
if(initial < 0)
throw std::runtime_error("Invalid value for semaphore");
}
// waits and decrements the value of the semaphore
void wait(int dec = 1)
{
while(_value < dec)
_cond.wait(l);
_value -= dec;
}
// increments the value of the semaphore
void signal(int inc = 1)
{
_value += inc;
_cond.broadcast();
}
};
// structure storing the bingo game data shared by the threads
struct bingo_data
{
// the current round of the game
atomic_int round;
// the numbers generated by the players
int number_count;
// the range for the generated numbers
int number_range;
// the number of players participating in the game
int number_of_players;
// indicates the number of players ready for the next step
semaphore players_ready;
// indicates that the server is ready for the next step
semaphore server_ready;
// indicates that a game round has finished
semaphore round_finished;
// the current number generated by the server read by the players
int current_number;
// the number of the winning player
atomic_int winner;
};
// the server thread main function
void* server_func(void* raw_ptr)
{
assert(raw_ptr);
bingo_data& game = *static_cast<bingo_data*>(raw_ptr);
// wait for the players to generate the numbers
// on their tickets and reset the winner
game.players_ready.wait(game.number_of_players);
// now all players have their tickets ready
// and the game can be started
while(!game.winner)
{
// wait until all players are ready to start the round
game.players_ready.wait(game.number_of_players);
// generate the number and signal the players
// that they can read the number
game.current_number = std::rand() % game.number_range;
game.server_ready.signal(game.number_of_players);
// now the players are checking their tickets
// so wait for them to finish
game.players_ready.wait(game.number_of_players);
// the game round has ended
++game.round;
game.round_finished.signal(game.number_of_players);
}
// wait for all players to quit
game.players_ready.wait(game.number_of_players);
return nullptr;
}
// the player thread main function
void* player_func(void* raw_ptr)
{
assert(raw_ptr);
bingo_data& game = *static_cast<bingo_data*>(raw_ptr);
// read the id of this thread
int id = game.current_number;
// signal that the id was read
game.players_ready.signal();
// the count of numbers from the ticket that were guessed
int guessed = 0;
// generate the unique numbers on the ticket
std::vector<int> ticket(game.number_count);
for(auto b=ticket.begin(),i=b; i!=ticket.end(); ++i)
{
while(true)
{
*i = std::rand() % game.number_range;
if(std::find(b, i, *i) == i) break;
}
}
// signal the server that this player is ready
game.players_ready.signal();
// and the game can begin
while(!game.winner)
{
game.players_ready.signal();
// wait for the server to generate new number
game.server_ready.wait();
// try to find the new number on the ticket
auto p = std::find(ticket.begin(), ticket.end(), game.current_number);
// if it was found
if(p != ticket.end())
{
// mark the number on the ticket as guessed
*p = -1;
// increase the count of guessed numbers
if(++guessed == game.number_count)
{
// if we guessed all numbers and there
// is no winner yet, set the winner
game.winner.set_if_zero(id);
}
}
game.players_ready.signal();
game.round_finished.wait();
}
// signal the server that the player ready to quit
game.players_ready.signal();
return nullptr;
}
int main(int argc, const char* argv[])
{
std::srand(::getpid());
// initialize the bingo game data
bingo_data game;
game.round = 0;
game.number_count = 20;
game.number_range = 1000;
game.number_of_players = 4;
game.current_number = 0;
game.winner = 0;
// start the players and detach them
while(game.current_number != game.number_of_players)
{
// the current_number is used here to store the
// id of the thread
++game.current_number;
frios::posix::thread(player_func, &game).detach();
game.players_ready.wait();
}
// start the server
frios::posix::thread server(server_func, &game);
// and wait for it to finish
server.wait_for();
// print the winner
std::cout << game.winner << std::endl;
return 0;
}