-2

I have written this code that simulates a parking system, however I have a problem when I have to reuse a struct that contains a messageQueue to my carthread. The problem occurs when I try to communicate to the handler for exit with ID_CAR_IND. The struct is send as a message, so I suspect that it gets deleted before arriving, but I can't seem to grasp what is happening, and where it goes wrong. It should be noted that it is a requirement implementation that pthread is used and 3 threads are created.

#include <iostream>
#include <pthread.h>
#include <stdlib.h>
#include <queue>
#include <time.h>
#include <unistd.h>
#include "Message.h"
#include "MsgQueue.h"


using namespace std;

enum{
    ID_START_IND,
    ID_ENTRY_REQ,
    ID_ENTRY_CFM,
    ID_CAR_IND,
    ID_STAY_HERE,
    ID_EXIT_REQ,
    ID_EXIT_OUT,
    ID_EXIT_CFM
    };

//MESSAGES
struct Car : public Message
{
    Car(int carID, MsgQueue* queue) : id(carID), carmq(queue){}
    MsgQueue *carmq;
    int id;
};

struct OpenReq : public Message
{
    MsgQueue *Who_is_asking_;
};

struct CloseReq : public Message
{
    MsgQueue *Who_is_asking_exit;
};

struct EntryDoorOpen : public Message{
    bool result;
};


MsgQueue entryMq(20);
MsgQueue exitMq(20);

void carHandler(Car* car, unsigned id, Message* msg){
    switch(id){
        case ID_START_IND:
        {
            cout << "car " << car->id << " wants to enter" << endl;
            OpenReq * req = new OpenReq();
            req->Who_is_asking_ = car->carmq;
            entryMq.send(ID_ENTRY_REQ, req);
        }
            break;

        case ID_ENTRY_CFM:
        {
            cout << "car " << car->id << " entered parking" << endl;
            entryMq.send(ID_CAR_IND);
        }
            break;

        case ID_STAY_HERE:
        {

        }
            break;

        case ID_EXIT_CFM:
        {
            cout << "car " << car->id << "Left parking" << endl;
            exitMq.send(ID_EXIT_OUT);
        }
            break;

        default:
            break;
    }
}

void entryHandler(unsigned id, Message* msg){

    OpenReq* req=static_cast<OpenReq*>(msg);

    switch(id){
        case ID_ENTRY_REQ:
        {
            cout << "Access granted. Opening entry door " << endl;

            req->Who_is_asking_->send(ID_ENTRY_CFM);
        }
            break;

        case ID_CAR_IND:
        {

            cout << "Closing entry door " << endl;
            sleep(2);
            req->Who_is_asking_->send(ID_EXIT_REQ);

        }
            break;

        default:
            break;
    }
}

void exitHandler(unsigned id, Message * msg)
{
    OpenReq* req = static_cast<OpenReq*>(msg);

    switch(id)
    {
        case ID_EXIT_REQ:
        {
            cout << "Leaving is Granted. Opening exit door" << endl;
            req->Who_is_asking_->send(ID_EXIT_CFM);
        }
            break;

        case ID_EXIT_OUT:
        {
            cout << "Car has left the parkinglot" << endl;
        }
            break;

        default:
            break;
    }
}

void *car(void* data){
    Car *car = static_cast<Car*>(data);
    car->carmq->send(ID_START_IND);
    for(;;){
        unsigned long id;
        Message *msg = car->carmq->receive(id);
        carHandler(car,id,msg);
        delete(msg);
    }
}

void *entry(void* data){
    for(;;){
        unsigned long id;
        Message *msg = entryMq.receive(id);
        entryHandler(id,msg);
        delete(msg);
    }
}

void *exit(void * data){
    for(;;){
        unsigned long id;
        Message *msg = exitMq.receive(id);
        exitHandler(id,msg);
        delete(msg);
    }

}


int main()
{
    MsgQueue q(10);

    Car carObj(1, &q);

    pthread_t carThread, entryThread;
    pthread_create(&carThread,nullptr,car, &carObj);
    pthread_create(&entryThread,nullptr,entry, nullptr);

    pthread_join(carThread,nullptr);


    return 0;
}
//
// Created by stud on 11/3/19.
//

#include "MsgQueue.h"
#include "Message.h"
#include <iostream>


MsgQueue::MsgQueue(unsigned long maxSize) : maxSize_(maxSize)
{
    //Init pthread funktionerne.
    pthread_mutex_init(&msgmutex, NULL);
    pthread_cond_init(&msgcond,NULL);
};

void MsgQueue::send(unsigned long id, Message* msg)
{
    pthread_mutex_lock(&msgmutex);

    while(msgqueue_.size() == maxSize_)
    {
        pthread_cond_wait(&msgcond, &msgmutex);
    }

    info besked;
    besked.id = id;
    besked.msg = msg;

    msgqueue_.push(besked);

    pthread_cond_broadcast(&msgcond);
    pthread_mutex_unlock(&msgmutex);

    //std::cout << "sending from id #" << id << std::endl;
};

Message* MsgQueue::receive(unsigned long&id)
{
    pthread_mutex_lock(&msgmutex);

    while(msgqueue_.empty())
    {
        pthread_cond_wait(&msgcond,&msgmutex);
    }
    info besked;

    besked = msgqueue_.front();
    id = besked.id;
    msgqueue_.pop();

    pthread_cond_broadcast(&msgcond);
    pthread_mutex_unlock(&msgmutex);

    return besked.msg;
};

MsgQueue::~MsgQueue()
{
    pthread_mutex_destroy(&msgmutex);
    pthread_cond_destroy(&msgcond);
};
//
// Created by stud on 11/3/19.
//

#pragma once

#include <iostream>
#include <pthread.h>
#include "Message.h"
#include <queue>

struct info : public Message
{
    unsigned long id;
    Message* msg;
};

class MsgQueue
{
public:
    MsgQueue(unsigned long maxSize);
    void send(unsigned long id, Message* msg = NULL);
    Message* receive(unsigned long&id);
    ~MsgQueue();

private:
    unsigned long maxSize_;
    std::queue <info> msgqueue_;
    pthread_cond_t msgcond;
    pthread_mutex_t msgmutex;
};
//
// 
//

#pragma once

class Message
{
public:
    virtual ~Message(){};
};
lenni
  • 1
  • 1
  • 2
  • 3
    Some of your functions have `void*` as return but never return anything, which is UB. – nada Nov 04 '19 at 15:48
  • Why are you using `pthread` rather than (portable) `std::thread`?? – Jesper Juhl Nov 04 '19 at 15:51
  • Are threads even *needed* or *wanted* here? It seems like the calculation done in each thread is negligible and doesn't outweigh the overhead and complexity of creating threads. Perhaps a simple, single threaded, state machine would be a better match for the problem.. – Jesper Juhl Nov 04 '19 at 15:57
  • @JesperJuhl It's a requirement for this exercise that you use pthreads and that you have three threads and handlers. – lenni Nov 04 '19 at 15:59
  • @Lena Your question should state that if it's a requirement. – Jesper Juhl Nov 04 '19 at 16:02

3 Answers3

0

The question currently lacks a few details (like details about what exactly your problem is, including any error message you get), but we can speculate some on what the problem is. Since the problem is with ID_CAR_IND, lets start by examining the handler for that message. It prints a message, then dereferences a pointer that is a property of the message. Nothing obviously wrong there.

So let's check where we create that message. It sends just the message ID. Furthere investigation shows that send takes an optional second parameter. Since it is not provided, this will be a nullptr in the message that is sent.

Since this extra message data is needed by the message handler, and not provided, this results in dereferencing a null pointer in the handler, resulting in Undefined Behavior and (typically) a crash of the program with something like an access violation error.

The solution is to pass an OpenReq object to send (like you are for the ID_START_IND message).

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
0

Consider revising the code to check or remove consecutive uses of the -> operator. There is some way in which the code cannot catch the null value.

C. R. Ward
  • 93
  • 5
0

Why don't you use OS API instead of implementing your own MsgQueue?