Reputation: 43
I'm new to threading in C++, and I'm sure it shows in my question here.
I'm working on a project for school, and I need to model a 4-way traffic intersection, where cars are threads. I have a clock counter, and every clock tick I generate a random number of cars, creating a simple Car object for each one, put that new car in one of 4 queues (streets) and then make a pthread to handle the car. Each car must wait until it gets to the front of its street, then wait for a semaphore that signals that a car can go. So if there's multiple cars waiting at the intersection, there's not any order as to which goes first.
I'm having issues, however. According to my printouts, a thread will run, exit, but then it will go back to waiting for the semaphore again. This segfaults when it gets the semaphore and tries to do stuff with the car. Any pointers in the right direction would be awesome!
A sample output is:
Running with 10 cars
| | |
2: Generating 3 cars. Already scheduled: 0, finished: 0
2: Generating car 2-0 at street 3
2: Generating car 2-1 at street 3
2: Generating car 2-2 at street 0
waiting for clk in main
2-2 waiting for turn
2: Car 2-2 driving from street 0
waiting for streets in departure
2-2 | | | 2-0, 2-1
| | | 2-0, 2-1
releasing streets in departure
2-2 releasing clock
2-2 exiting
got clk in main
| | | 2-0, 2-1
4: Generating 3 cars. Already scheduled: 3, finished: 1
2-22-2 waiting for turn
4: Generating car 4-0 at street 1
waiting for turn
4: Generating car 4-1 at street 1
4: Generating car 4-2 at street 2
4-2 waiting for turn
waiting for clk in main
4: Car 2-2 driving from street 0
waiting for streets in departure
| 4-0, 4-1 | 4-2 | 2-0, 2-1
Segmentation fault (core dumped)
My main method:
int numCars = 0;
deque<deque<Car> > streets; //each queue for each street
bool empty = true; // intersection is empty (Boolean)
int clk = 0; // clock counter
int carsFinished = 0;
int carsScheduled = 0;
sem_t turn;
sem_t streetSem;
sem_t sigSem;
sem_t clkSem;
int main () {
deque<Car> street0, street1, street2, street3;
streets.push_back(street0);
streets.push_back(street1);
streets.push_back(street2);
streets.push_back(street3);
printf("Please enter the number of cars to run: ");
cin >> numCars;
cout << "\nRunning with " << numCars << " cars\n";
sem_init(&turn, 0, 0);
sem_init(&streetSem, 0, 1);
sem_init(&sigSem, 0, 0);
sem_init(&clkSem, 0, 0);
while (carsFinished < numCars) {
// Generate a random number of cars, from 0 to 5
double r = rndom();
int numCarsThisRound;
if (r == 1.0) {
numCarsThisRound = 5; // so it won't go to 6
} else {
numCarsThisRound = r * 6.0;
}
int i = 0;
if ((carsScheduled < numCars) && ((carsScheduled - carsFinished) < 110) && (i < numCarsThisRound)) {
sem_wait(&streetSem);
print_streets();
sem_post(&streetSem);
cout << clk << ": Generating " << numCarsThisRound << " cars. Already scheduled: " << carsScheduled << ", finished: " << carsFinished << "\n";
}
// there are still cars there aren't more than 110 cars waiting we don't go over the number of cars for this clock
while ((carsScheduled < numCars) && ((carsScheduled - carsFinished) < 110) && (i < numCarsThisRound)) {
// assign each car to a random street
double r = rndom();
int num;
if (r == 1.0) {
num = 3; // so it won't go to 4
} else {
num = r * 4.0;
}
// make new car
Car car;
car.setQueue(num);
std::ostringstream s;
s << clk << "-" << i;
std::string id(s.str());
car.setId(id);
cout << clk << ":\tGenerating car " << car.id << " at street " << num << "\n";
sem_wait(&streetSem);
// if this is the first car in a street, let it know
if (streets[num].empty())
car.isFront = true;
streets[num].push_back(car);
sem_post(&streetSem);
// make new thread for that car
pthread_t pt;
pthread_create(&pt, NULL, &arrival, (void *)&car);
carsScheduled++;
i++;
}
// if there's a car that should go this clk
if (!streetsAreEmpty()) {
// wait until that car runs before incrementing clk
sem_post(&turn);
cout << "waiting for clk in main\n";
sem_wait(&clkSem);
cout << "got clk in main\n";
}
clk++;
}
return (0);
}
Other methods the threads use:
// car arrives at street
void* arrival(void *v) {
Car car = *(Car*)v;
// block until I'm at the front of the queue
while (true) {
if (car.isFront) {
break;
}
}
cout << car.id << " waiting for turn\n";
sem_wait(&turn); // wait for it to be someone's turn
cout << car.id << " waiting for signal\n";
cout << clk << ": Car " << car.id << " driving from street " << car.queue << "\n";
drive();
departure(car.queue);
cout << car.id << " releasing clock\n";
sem_post(&clkSem); // let the main method know I'm done
cout << car.id << " exiting\n";
pthread_exit(NULL);
}
// departure of car from intersection
void departure(int i) {
// one car departs
cout << "waiting for streets in departure\n";
sem_wait(&streetSem);
print_streets();
streets[i].pop_front();
if (!streets[i].empty()) {
streets[i].front().isFront = true;
}
print_streets();
cout << "releasing streets in departure\n";
sem_post(&streetSem);
carsFinished++;
}
// take up a clock tick to simulate driving
void drive() {
clk++;
}
The Car class header - very simple:
#ifndef CAR_H
#define CAR_H
class Car {
#include <string>
public:
Car();
Car(int);
void setQueue(int);
void setId(std::string);
std::string id;
int queue;
bool isFront;
};
#endif
Thanks!
Upvotes: 1
Views: 978
Reputation: 96258
This is undefined behaviour.
Car car
is a local variable and it gets destroyed once it falls out of scope.
You're assuming that a copy is made (Car car = *(Car*)v;
) before that happens, but that isn't necessarily true.
Change the life-time of the car, there should be (a heap allocated?) one per thread.
Upvotes: 1