#diverging pthreads logic

61 messages · Page 1 of 1 (latest)

solid heron
#

I have to match a simple matching game in CPP utilizing pthread architecture. I am trying to solve how I can block 5 of the 6 player threads until the 1 dealer thread finishes dealing cards

opal lagoonBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

solid heron
#

my code is as follows:

/*
6 players, 1 of them will be the dealer depending on round
round 1 = player 1 is the dealer
1 deck of cards - vector
dealer draws one card, called target card
dealer give each player 1 card
"dealer places cards on table to draw from" - mutex protected deck
players then take turns in RR (ignoring dealer) pulling 1 card from the deck
if 1 of the players card matches the target card, they win and the round ends
else if neither match, discards 1 card at random (card is placed at the end of the deck), then passes to the next player
when rounds ends next player is the dealer
*/

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
//#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>
#include <vector>
#include <algorithm>
#include <ctime>

using namespace std;

vector<int> deck;
vector< vector<int> > playerHands;
volatile int target_card;
volatile int rounds = 0;

// create a mutex
pthread_mutex_t deck_mutex;
pthread_mutex_t turn_mutex;
pthread_mutex_t round_mutex;
pthread_barrier_t barrier;

// Initialize threads
pthread_t* const handle = new pthread_t[6];

static void* game( void* arg );
static void dealer( long my_rank );
static void player( long my_rank );

int main(){
    //fill the deck 1-13, face cards are 11, 12, 13
    for( int i = 1; i <= 13; i++){
        // push 1 value for each suit
        deck.push_back( i );
        deck.push_back( i );
        deck.push_back( i );
        deck.push_back( i );
    }

    // Shuffle the vector
    srand(time(NULL));
    random_shuffle(deck.begin(), deck.end());

    // Print the shuffled deck (for testing purposes)
    for (const auto &card : deck) {
        cout << card << " ";
    }

    //initialize players hands vectors
    for( int i = 0; i < 6; i++){
        // Pushes a epmty vector for each players hand
        // locate each player via vector index playerHands[player # - 1]
        playerHands.push_back(vector<int>());
    }

    
    pthread_mutex_init( &deck_mutex, NULL );
    pthread_mutex_init( &turn_mutex, NULL );
    pthread_mutex_init( &round_mutex, NULL );
    pthread_barrier_init(&barrier, NULL, 6);

    
    for( int i = 0; i < 6; i++ ){
        pthread_create(&handle[i], NULL, game, (void*)i);
    }
}


static void* game( void* arg ){
    const long my_rank = (long)arg;
    for( int i = 0; i < 6; i++){
        //pick dealer
        if( my_rank == rounds ){
            dealer( my_rank );
        }
        //pick players
        if( my_rank != rounds ){
            player( my_rank );
        }
    }


    return NULL;
}

static void dealer( long my_rank ){
    cout << endl << my_rank << endl;
    pthread_mutex_lock( &deck_mutex );
    //pick a target card from top of deck
    target_card = deck.front();
    deck.erase(deck.begin());

    //deal a card to each players hand
    for( int i = 0; rounds < 6; i++ ){
        if ( i != rounds ){
            playerHands[i].push_back(deck.front());
            deck.erase(deck.begin());
        }
    }

    pthread_mutex_unlock(&deck_mutex);

    // Players do the rest of the functionality
    pthread_barrier_wait(&barrier);
}

static void player( long my_rank ){
    
    // Buffer that will stop all threads until all 6 reach barrier
    pthread_barrier_wait(&barrier);
}
opal bison
#

why are you using pthreads

solid heron
#

we're supposed to simulate the game as if there are actual players. its supposed to be practice using POSIX threads, but we never covered using divergence

#

we only practiced using parallel programming to supplementary complete a task

#

my primary concern rn is if i can call pthread_barrier_wait(&barrier); in 2 places and have have them work/release the threads

#

cuz chatgpt is telling me no, but its pretty innacurate with pthreads, and debugging is also challenging with pthreads

opal bison
#

firstly:

#

you have rounds defined as a volatile int initialized to 0

#

it never changes

#

so

#
        if ( i != rounds ){
            playerHands[i].push_back(deck.front());
            deck.erase(deck.begin());
        }
    }```
#

0 is always less than 6

#

so this'll go on forever

#

or until i is not a valid index of playerHands, which, in your case, is when it is 7

solid heron
#

i still needed to increment rounds when a winner is determined, but im focusing on how to plock player threads until dealer pushes a hard into each of the players hands/ playerhands vector

opal bison
#

so you're asking how to block the pthreads until a condition is met?

solid heron
#

yeah, cuz the dealer has to deal first, but the dealer() function wont always be called first

solid heron
opal bison
#

pthread_join is perfect for this

solid heron
#

but that reduces all the threads, i would have to launch them all again, which is a strategy but i dont think it solves the issue that the dealer has to act first despite not being called first

opal bison
#

if the dealer thread is running once

#

it doesn't have to be its own thread at all

#

why not run the dealer function and THEN launch the player threads?

solid heron
#

the puzzle about this problem is that each thread will have a turn as the dealer

opal bison
#

in that case, what you have currently looks fine

#

the barrier won't be surpassed until the dealer thread is finished

solid heron
#

i want to double check if the way im using barrier is gonna work, cuz in other projects i call barrier wait ina pthread function that all the threads run, this time i got 1 thread in dealer and 5 threads in player, i dont want to cause a deadlock

#

so im also looking at pthread_cond_wait() to see how to use that

opal bison
#

if there's any deadlock, it'll be a result of the pthread source code if you're using pthread_barrier_wait lol

#

you're writing very c-like c++

#

what does the variable rounds represent? it is the number of rounds that'll occur?

solid heron
#

i am going to use it to track current round, maybe i should rename it to completed_rounds for clarification

#

but because player 1 is the dealer round 1, and player 2 is the dealer round 2, i can just use it as an index for which thread runs the dealer function that round

#

the number of rounds that occur is just a hard coded 6

opal bison
#

is the number of people playing always 6?

#

do they specifically want you to use a vector of vectors for the player's hand?

solid heron
#

the number is always 6 and i chose a vector of vectors cuz i thought it would be easier to add and remove from

#

want able to figure out the condition variables so im going to keep using barriers and hope it works they way i want it to

#

ima step away to shower off, my current plan of action is as follows, ill see how it works later:

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
//#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>
#include <vector>
#include <algorithm>
#include <ctime>

using namespace std;

vector<int> deck;
vector< vector<int> > playerHands;
volatile int target_card;
volatile bool winner = false;
volatile int completed_rounds = 0;

// create a mutex
pthread_mutex_t deck_mutex;
pthread_mutex_t turn_mutex;
pthread_mutex_t round_mutex;
pthread_barrier_t barrier;

// Initialize threads
pthread_t* const handle = new pthread_t[6];

static void* game( void* arg );
static void dealer( long my_rank );
static void player( long my_rank );

int main(){
    //fill the deck 1-13, face cards are 11, 12, 13
    for( int i = 1; i <= 13; i++){
        // push 1 value for each suit
        deck.push_back( i );
        deck.push_back( i );
        deck.push_back( i );
        deck.push_back( i );
    }

    // Shuffle the vector
    srand(time(NULL));
    random_shuffle(deck.begin(), deck.end());

    // Print the shuffled deck (for testing purposes)
    for (const auto &card : deck) {
        cout << card << " ";
    }

    //initialize players hands vectors
    for( int i = 0; i < 6; i++){
        // Pushes a epmty vector for each players hand
        // locate each player via vector index playerHands[player # - 1]
        playerHands.push_back(vector<int>());
    }

    
    pthread_mutex_init( &deck_mutex, NULL );
    pthread_mutex_init( &turn_mutex, NULL );
    pthread_mutex_init( &round_mutex, NULL );
    pthread_barrier_init(&barrier, NULL, 6);

    
    for( int i = 0; i < 6; i++ ){
        pthread_create(&handle[i], NULL, game, (void*)i);
    }
}


static void* game( void* arg ){
    const long my_rank = (long)arg;
    for( int i = 0; i < 6; i++){
        //pick dealer
        if( my_rank == completed_rounds ){
            dealer( my_rank );
        }
        //pick players
        if( my_rank != completed_rounds ){
            player( my_rank );
        }
    }


    return NULL;
}

static void dealer( long my_rank ){
    cout << endl << my_rank << endl;

    pthread_mutex_lock( &deck_mutex );
    //pick a target card from top of deck
    target_card = deck.front();
    deck.erase(deck.begin());

    //deal a card to each players hand
    for( int i = 0; i < 6; i++ ){
        if ( i != completed_rounds ){
            playerHands[i].push_back(deck.front());
            deck.erase(deck.begin());
        }
    }

    pthread_mutex_unlock(&deck_mutex);

    // Players do the rest of the functionality
    pthread_barrier_wait(&barrier);
}

static void player( long my_rank ){
    
    // Buffer that will stop all threads until all 6 reach barrier
    pthread_barrier_wait(&barrier);
    bool did_i_win = false;

    //players then take turns in RR (ignoring dealer) pulling 1 card from the deck
    
    while( !winner ){
        pthread_mutex_lock( &deck_mutex );
        //double check if theres not a winner
        if( !winner ){
            //pull a card from the deck and add to players hand
            //check to see if either of the cards in players hand match the target card
            //if a card matches then this player wins, winner = true, completed_rounds++, puts both cards back into the deck
            //else this player didnt win and puts 1 card back into the deck at random
        }
        else {
            // a player already won
            // return the cards to the back of the deck
        }
        pthread_mutex_lock( &deck_mutex );
    }
}
opal bison
#

and now i see what you're doing with completed_rounds - it's very finnicky, however

#

have you been introduced to creating your own classes yet?

solid heron
#

i dont really see a purpose for adding classes or structs to this task

#

i could make a card struct but i only need a int value for my output

solid heron
#

ok so this is what i have

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
//#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <queue>
#include <vector>
#include <algorithm>
#include <ctime>

using namespace std;

vector<int> deck;
vector<int> playerHands[6];
static int target_card;
volatile bool winner = false;
volatile int completed_rounds = 0;

// create a mutex
pthread_mutex_t deck_mutex;
pthread_mutex_t turn_mutex;
pthread_mutex_t round_mutex;
pthread_barrier_t barrier;

// Initialize threads
pthread_t* const handle = new pthread_t[6];

static void* game( void* arg );
static void dealer( long my_rank );
static void player( long my_rank );

int main(){
    //fill the deck 1-13, face cards are 11, 12, 13
    for( int i = 1; i <= 13; i++){
        // push 1 value for each suit
        deck.push_back( i );
        deck.push_back( i );
        deck.push_back( i );
        deck.push_back( i );
    }

    // Shuffle the vector
    srand(time(NULL));
    random_shuffle(deck.begin(), deck.end());

    // Print the shuffled deck (for testing purposes)
    for (const auto &card : deck) {
        cout << card << " ";
    }

    //initialize players hands vectors
    for( int i = 0; i < 6; i++){
        // Pushes a epmty vector for each players hand
        // locate each player via vector index playerHands[player #]
        playerHands[i] = vector<int>();
    }

    
    pthread_mutex_init( &deck_mutex, NULL );
    pthread_mutex_init( &turn_mutex, NULL );
    pthread_mutex_init( &round_mutex, NULL );
    pthread_barrier_init(&barrier, NULL, 6);

    
    for( int i = 0; i < 6; i++ ){
        pthread_create(&handle[i], NULL, game, (void*)i);
    }
}
#
static void* game( void* arg ){
    const long my_rank = (long)arg;
    for( int i = 0; i < 6; i++){
        //pick dealer
        if( my_rank == completed_rounds ){
            dealer( my_rank );
        }
        //pick players
        if( my_rank != completed_rounds ){
            player( my_rank );
        }
    }
    return NULL;
}
#
static void dealer( long my_rank ){
    cout << endl << my_rank << endl;

    pthread_mutex_lock( &deck_mutex );
    //pick a target card from top of deck
    target_card = deck.front();
    deck.erase(deck.begin());

    cout << "PLAYER " << my_rank + 1 << " is DEALER: target card is " << target_card << endl;

    //deal a card to each players hand
    for( int i = 0; i < 6; i++ ){
        if ( i != completed_rounds ){
            playerHands[i].push_back(deck.front());
            deck.erase(deck.begin());
        }
    }

    pthread_mutex_unlock(&deck_mutex);

    // Players do the rest of the functionality
    pthread_barrier_wait(&barrier);
}
#
static void player( long my_rank ){
    
    // Buffer that will stop all threads until all 6 reach barrier
    pthread_barrier_wait(&barrier);
    bool did_i_win = false;

    //players then take turns in RR (ignoring dealer) pulling 1 card from the deck
    
    while( !winner ){
        pthread_mutex_lock( &deck_mutex );
        //double check if theres not a winner
        if( !winner ){
            cout << "PLAYER " << my_rank + 1 << ": hand (" << playerHands[my_rank].front() << ")" << endl;
            //pull a card from the deck and add to players hand
            int card = deck.front();
            deck.erase(deck.begin());
            playerHands[my_rank].push_back(card);

            cout << "PLAYER " << my_rank + 1 << ": draws " << card << endl;
            cout << "PLAYER " << my_rank + 1 << ": hand (" << playerHands[my_rank].front() << "," << playerHands[my_rank].back() << ")"
            << "<> target card is " << target_card << endl;

            //check to see if either of the cards in players hand match the target card
            for (int i = 0; i < playerHands[my_rank].size(); i++) {
                //if a card matches then this player wins, winner = true, completed_rounds++, puts both cards back into the deck
                if (playerHands[my_rank][i] == target_card){
                    did_i_win = true;
                    completed_rounds++;
                    //put back target card
                    deck.push_back( target_card );
                    //put back both cards from player's hand
                    card = playerHands[my_rank].front();
                    playerHands[my_rank].erase(playerHands[my_rank].begin());
                    deck.push_back( card );

                    card = playerHands[my_rank].front();
                    playerHands[my_rank].erase(playerHands[my_rank].begin());
                    deck.push_back( card );
                }
            }
            //else this player didnt win and puts 1 card back into the deck at random
            if (!did_i_win) {
                // Player didn't win, put 1 card back into the deck at random
                int random_index = rand() % playerHands[my_rank].size();
                cout << "PLAYER " << my_rank + 1 << ": discards " << playerHands[my_rank][random_index] << " at random" << endl;

                deck.push_back(playerHands[my_rank][random_index]);
                playerHands[my_rank].erase(playerHands[my_rank].begin() + random_index);
            }
        }
        else {
            // a player already won
            // return the cards to the back of the deck
            int card = playerHands[my_rank].front();
            playerHands[my_rank].erase(playerHands[my_rank].begin());
            deck.push_back( card );
        }
        pthread_mutex_lock( &deck_mutex );
    }
    if( did_i_win ){
        cout << "PLAYER " << my_rank + 1 << ": won round " << completed_rounds << endl;
    }
    else{
        cout << "PLAYER " << my_rank + 1 << ": lost round " << completed_rounds << endl;
    }
}
#

and what i get is:
[qgk11@eros ~]$ ./matching
4 12 1 8 6 10 2 4 7 6 2 11 13 7 5 7 9 5 2 10 8 1 3 3 13 13 6 3 12 13 9 12 4 5 11 9 1 8 12 2 8 1 6 11 3 10 7 9 11 4 5 10
0
PLAYER 1 is DEALER: target card is 4
PLAYER 3: hand (-1709831264)

#

however i did just notice i did not clean up correctly

#

and a typo on unlocking mutex

solid heron
#

what am i not understanding about vectors that is causing conditions not to work?

solid heron
#

made more alterations and getting closer to working results

#

i may need to rethink my architecture. I dont know how to control the order of the threads, some threads let go of the mutex but then acquire it again immediately, when i thought mutexes were first come first server

solid heron
#

😦

solid heron
#

!solved