Producers, Consumers, and my cute Deadlock - c++

I'm working on a Producer Consumer (Multithreaded) problem and I'm encountering a deadlock. My question is how?
I have multiple producers creating n products, and putting them in a global queue. The producers must WAIT if there is no room in the queue.
My consumers are accessing the queue by using First Come First Serve. The consumer must WAIT if there is nothing in the queue. The consumer will consume PART of the product, and the product will only be removed from the queue when it has been totally consumed. The consumers will stop consuming when there are no more products to be consumed.
I get the deadlock when the first item is done being consumed. I am using a mutex to lock the queue, 2 condition variables to signal when an item is added or removed to the queue, and a semaphore to keep track of total items being consumed.
Any idea why it may be deadlocking?
Input: 2 (Producers) 3 (Consumers) 5 (Items to produce) 2 (Queue Size) 0 (N/A) 50 (Quantum) 1 (Seed)
Producer:
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
#include <iostream>
#include <sys/time.h>
void* producer(void* args)
{
tProducer producer = *((tProducer*) args);
tProduct products[producer.products];
unsigned int queueMax = producer.queueMax;
timeval time;
std::string output;
for(int i = 0; i < producer.products; i++)
{
//Create item
products[i].productId = i;
products[i].life = producer.lifeOfProduct;
gettimeofday(&time, NULL);
products[i].timestamp = time.tv_sec;
//Lock and add to queue
pthread_mutex_lock(&queueLock);
//Queue is full and must wait
if(queue.size() >= queueMax)
{
output = "Producer: " + std::to_string(producer.id) + " is waiting\n";
std::cout << output;
pthread_cond_wait(&removeSignal, &queueLock);
}
//Debug message
output = "Producer: " + std::to_string(producer.id) + " is producing.\n";
std::cout << output;
//Add item to queue and signal
queue.push(products[i]);
pthread_cond_signal(&addSignal);
pthread_mutex_unlock(&queueLock);
//pthread_cond_signal(&addSignal);
//Debug message
output = "Producer: " + std::to_string(producer.id) + " just produced.\n";
std::cout << output;
}
pthread_exit(NULL);
}
Consumer:
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
#include <iostream>
void* consumer(void* args)
{
tConsumer consumer = *((tConsumer*) args);
int id = consumer.id;
int quantum = consumer.quantum;
std::string output;
while(true)
{
//Exit when nothing else is being created
if(sem_trywait(&totalProductsLeft) < 0)
{
break;
}
//Subtract life from product, and remove from queue if done
pthread_mutex_lock(&queueLock);
//Wait until item is in queue
if(queue.size() <= 0)
{
//Debug message
output = "Consumer: " + std::to_string(id) + " is waiting.\n";
std::cout << output;
pthread_cond_wait(&addSignal, &queueLock);
}
//Debug message
output = "Consumer: " + std::to_string(id) + " is ready.\n";
std::cout << output;
tProduct& product = queue.front();
product.life -= quantum;
//Item is done being consumed
if(product.life <= 0)
{
//Debug message
output = "Product: " + std::to_string(product.productId) + " is dead.\n";
std::cout << output;
//Remove a spot
queue.pop();
pthread_cond_signal(&removeSignal);
sem_wait(&totalProductsLeft);
}
else
{
//Debug message
output = "Product: " + std::to_string(product.life) + "hp is not done.\n";
std::cout << output;
}
pthread_mutex_unlock(&queueLock);
}
//May need to broadcast
pthread_exit(NULL);
}
Main (Just to show how I initialize everything):
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
std::queue<tProduct> queue;
pthread_cond_t addSignal;
pthread_cond_t removeSignal;
sem_t totalProductsLeft;
pthread_mutex_t queueLock;
int main(int argc, char** argv)
{
//Handle input
const int NUM_INPUTS = 8;
int numberProducers;
int numberConsumers;
int numberOfProducts;
int queueSize;
int scheduleType;
int quantum;
int seed;
//Error check for input
if(argc != NUM_INPUTS)
{
std::cout << "Invalid number of arguments.\n";
return -1;
}
//Grab arguments
numberProducers = atoi(argv[1]);
numberConsumers = atoi(argv[2]);
numberOfProducts = atoi(argv[3]);
queueSize = atoi(argv[4]);
scheduleType = atoi(argv[5]);
quantum = atoi(argv[6]);
seed = atoi(argv[7]);
//Get rid of warnings for now
std::cout << numberOfProducts << std::endl;
std::cout << queueSize << std::endl;
std::cout << quantum << std::endl;
std::cout << seed << std::endl;
std::cout << scheduleType << std::endl;
//Create threads
pthread_t producerThreads[numberProducers];
pthread_t consumerThreads[numberConsumers];
tProducer producerArgs[numberProducers];
tConsumer consumerArgs[numberConsumers];
//Initialize global
pthread_mutex_init(&queueLock, NULL);
pthread_cond_init(&addSignal, NULL);
pthread_cond_init(&removeSignal, NULL);
std::cout << "Total Items: " << (numberProducers * numberOfProducts) << std::endl;
sem_init(&totalProductsLeft, 0, numberProducers * numberOfProducts);
//Start threads
srand(seed);
for(int i = 0; i < numberProducers; i++)
{
producerArgs[i].id = i;
producerArgs[i].products = numberOfProducts;
producerArgs[i].lifeOfProduct = rand() % 1024;
producerArgs[i].queueMax = queueSize;
pthread_create(&(producerThreads[i]), 0, producer, &producerArgs[i]);
}
for(int i = 0; i < numberConsumers; i++)
{
consumerArgs[i].id = i;
consumerArgs[i].quantum = quantum;
pthread_create(&(consumerThreads[i]), 0, consumer, &consumerArgs[i]);
}
//Wait for threads to end
for(int i = 0; i < numberProducers; i++)
{
pthread_join(producerThreads[i], NULL);
}
for(int i = 0; i < numberConsumers; i++)
{
pthread_join(consumerThreads[i], NULL);
}
return 0;
}

I ended up figuring it out. sem_trywait, in my consumer, is decrementing when items weren't done being consumed. The sem_wait inside my consumer is then blocking because there are no items left.

Related

Getting memory usage of program from another program in C++ (LINUX)

I would like to measure the maximum memory usage of abc.exe on random tests generated by gen.exe. How could I do that?
My code that runs abc.exe on tests from gen.exe looks like this:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int i = 0;
while (true)
{
string si = to_string(i);
cout << i << "\n";
if (system(("echo " + si + "| ./gen.exe > test.in").c_str())) // gen.exe is test generator
{
cout << "gen error\n";
break;
}
if (system(("./abc.exe < test.in > a.out"))) // abc.exe is the program I want to test
{
cout << "abc error\n";
break;
}
i++;
}
}
I know that i can use time -v ./abc.exe but then the used memory is printed in the terminal but I'd like to be able to save it to a variable.
You can use getrusage( RUSAGE_CHILDREN, ... ) to obtain the maximum resident memory. Note that this call will return the maximum memory used by the biggest child at that point in time.
In the example below I used boost::process because it gives better control but it's up to you to use std::system or not, works the same way.
#include <string>
#include <cstdint>
#include <string.h>
#include <iostream>
#include <boost/process/child.hpp>
#include <sys/resource.h>
namespace bp = boost::process;
int parent( const std::string& exename )
{
// Loop from 0 to 10 megabytes
for ( int j=0; j<10; ++j )
{
// Command name is the name of this executable plus one argument with size
std::string gencmd = exename + " " + std::to_string(j);
// Start process
bp::child child( gencmd );
// Wait for it to allocate memory
sleep(1);
// Query the memory usage at this point in time
struct rusage ru;
getrusage( RUSAGE_CHILDREN, &ru );
std::cerr << "Loop:" << j << " mem:"<< ru.ru_maxrss/1024. << " MB" << std::endl;
// Wait for process to quit
child.wait();
if ( child.exit_code()!=0 )
{
std::cerr << "Error executing child:" << child.exit_code() << std::endl;
return 1;
}
}
return 0;
}
int child( int size ) {
// Allocated "size" megabites explicitly
size_t memsize = size*1024*1024;
uint8_t* ptr = (uint8_t*)malloc( memsize );
memset( ptr, size, memsize );
// Wait for the parent to sample our memory usage
sleep( 2 );
// Free memory
free( ptr );
return 0;
}
int main( int argc, char* argv[] )
{
// Without arguments, it is the parent.
// Pass the name of the binary
if ( argc==1 ) return parent( argv[0] );
return child( std::atoi( argv[1] ) );
}
It prints
$ ./env_test
Loop:0 mem:0 MB
Loop:1 mem:3.5625 MB
Loop:2 mem:4.01953 MB
Loop:3 mem:5.05469 MB
Loop:4 mem:6.04688 MB
Loop:5 mem:7.05078 MB
Loop:6 mem:7.78516 MB
Loop:7 mem:8.97266 MB
Loop:8 mem:9.82031 MB
Loop:9 mem:10.8867 MB
If you cannot use boost libraries, you'd got to work a little more but it is still feasible.
If you just want to know the maximum size ever of your children processes then the following works with std::system:
#include <cstdio>
#include <string>
#include <iostream>
#include <sstream>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
int main(int argc, char* argv[]) {
if (argc > 1) {
size_t size = ::atol(argv[1]);
size_t memsize = size * 1024 * 1024;
void* ptr = ::malloc(memsize);
memset(ptr, 0, memsize);
::sleep(2);
::free(ptr);
return 0;
}
for (int j = 0; j < 10; ++j) {
std::ostringstream cmd;
cmd << argv[0] << " " << j;
int res = std::system(cmd.str().c_str());
if (res < 0) {
fprintf(stderr, "ERROR system: %s\n", strerror(errno));
break;
}
struct rusage ru;
res = getrusage(RUSAGE_CHILDREN, &ru);
size_t maxmem = ru.ru_maxrss;
fprintf(stderr, "Loop:%d MaxMem:%ld\n", j, maxmem);
}
return 0;
}
It prints
Loop:0 MaxMem:3552
Loop:1 MaxMem:4192
Loop:2 MaxMem:5148
Loop:3 MaxMem:6228
Loop:4 MaxMem:7364
Loop:5 MaxMem:8456
Loop:6 MaxMem:9120
Loop:7 MaxMem:10188
Loop:8 MaxMem:11324
Loop:9 MaxMem:12256
However if you want to keep track of the memory usage during the child process execution you cannot use std::system(). First, you need to call fork() to spawn a new process and then execv() to execute a bash command.
#include <string>
#include <cstdint>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <sys/resource.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <vector>
int parent(const std::string& exename) {
// Loop from 0 to 10 megabytes
for (int j = 0; j < 10; ++j) {
// Command name is the name of this executable plus one argument with size
std::string gencmd = exename + " " + std::to_string(j);
// Start process
pid_t pid = fork();
if (pid == 0) { // child
const char* args[] = {"/bin/bash", "-c", gencmd.c_str(), (char*)0};
int res = execv("/bin/bash", (char**)args);
// Should never return
std::cerr << "execv error: " << strerror(errno) << std::endl;
return 1;
}
// parent
long maxmem = 0;
while (true) {
int status;
pid_t rid = ::waitpid(pid, &status, WNOHANG);
if (rid < 0) {
if (errno != ECHILD) {
std::cerr << "waitpid:" << strerror(errno) << std::endl;
return 2;
}
break;
}
if (rid == pid) {
if (WIFEXITED(pid)) {
break;
}
}
// Wait for it to allocate memory
usleep(10000);
// Query the memory usage at this point in time
struct rusage ru;
int res = getrusage(RUSAGE_CHILDREN, &ru);
if (res != 0) {
if (errno != ECHILD) {
std::cerr << "getrusage:" << errno << strerror(errno) << std::endl;
}
break;
}
if (maxmem < ru.ru_maxrss) {
maxmem = ru.ru_maxrss;
}
}
std::cerr << "Loop:" << j << " mem:" << maxmem / 1024. << " MB" << std::endl;
}
return 0;
}
int child(int size) {
// Allocated "size" megabites explicitly
size_t memsize = size * 1024 * 1024;
uint8_t* ptr = (uint8_t*)malloc(memsize);
memset(ptr, size, memsize);
// Wait for the parent to sample our memory usage
sleep(2);
// Free memory
free(ptr);
return 0;
}
int main(int argc, char* argv[]) {
// Without arguments, it is the parent.
// Pass the name of the binary
if (argc == 1) return parent(argv[0]);
return child(std::atoi(argv[1]));
}
The result on my machine is:
$ ./fork_test
Loop:0 mem:3.22656 MB
Loop:1 mem:3.69922 MB
Loop:2 mem:4.80859 MB
Loop:3 mem:5.92578 MB
Loop:4 mem:6.87109 MB
Loop:5 mem:8.05469 MB
Loop:6 mem:8.77344 MB
Loop:7 mem:9.71875 MB
Loop:8 mem:10.7422 MB
Loop:9 mem:11.6797 MB
There is a video about this post.

Creating Multiple Processes and Communicating with Pipes in C++

I am attempting to create two child processes with a shared parent, the parent must print the output of each of the child processes, It sends a list of numbers to the Child A, child A sorts these numbers, and sends the sorted list to both the parent and child B, child B then finds the median of the list of numbers, and sends it to the parent where it prints both results.
The problem I'm running into is that it executes fine for Child A and prints child A's results, but does not print anything for child b.
#include <string>
#include <vector>
#include <cstdlib>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char **argv)
{
vector<int> numbers;
vector<int> sortedVector;
if (argc == 6)
{
//Create vector
numbers.push_back(atoi(argv[1]));
numbers.push_back(atoi(argv[2]));
numbers.push_back(atoi(argv[3]));
numbers.push_back(atoi(argv[4]));
numbers.push_back(atoi(argv[5]));
}
else
{
cout << "Error\n";
}
int fd[2];
int fd1[2];
if (pipe(fd) == -1 || pipe(fd1) == -1)
{
std::cout << "Pipe failed.\n";
}
int child_a, child_b;
child_a = fork();
//Child A process
if (child_a == 0)
{
//Child A code
sortedVector = numbers;
sort(sortedVector.begin(), sortedVector.end());
int array1[5] = { sortedVector.at(0), sortedVector.at(1), sortedVector.at(2), sortedVector.at(3), sortedVector.at(4) };
//Send to parent
close(fd[0]);
write(fd[1], array1, 5*sizeof(int));
close(fd[1]);
//Send to B
close(fd1[0]);
write(fd1[1], array1, 5*sizeof(int));
close(fd1[1]);
}
else
{
child_b = fork();
if (child_b == 0)
{
//Child B code
cout << "Array from A ";
//Read from A
int arrayFromA[5] = { 0, 0, 0, 0, 0 };
close(fd1[1]);
read(fd1[0], arrayFromA, 5*sizeof(int));
close(fd1[0]);
//Find median
double median;
int arraySize = sizeof(arrayFromA) / sizeof(arrayFromA[0]);
median = (double)(arrayFromA[(arraySize - 1) / 2] + arrayFromA[arraySize / 2]) / 2.0;
//Send to Parent
close(fd1[0]);
write(fd1[1], &median, sizeof(double));
close(fd1[1]);
}
else
{
//Parent code
//Read from Child A
vector<int> vectorFromChild;
close(fd[1]);
int array2[5] = { 0, 0, 0, 0, 0 };
read(fd[0], array2, 5*sizeof(int));
close(fd[0]);
std::cout << "The sorted list: ";
for (int i = 0; i < 5; i++)
{
if (i != 4)
{
std::cout << array2[i] << ", ";
}
else
{
std::cout << array2[i] << ".";
}
}
std::cout << std::endl;
//Read from Child B
double medianOutput;
close(fd1[1]);
read(fd1[0], &medianOutput, sizeof(double));
close(fd1[0]);
std::cout << "The median of the list is: " << medianOutput << std::endl;
wait(NULL);
}
}
}
It won't even print: "The median of list is: "
In Child B code, you close(fd[1]) at line 68, then write to it at line 80: write(fd1[1], &median, sizeof(double));.
UNIX gave us return values for a reason.

Multiple Definitions Error of Global Arrays [duplicate]

This question already has answers here:
c++ multiple definitions of a variable
(5 answers)
multiple definition error c++
(2 answers)
What exactly is One Definition Rule in C++?
(1 answer)
Closed 2 years ago.
I am attempting to compile my c++ code, and I continue getting the error:
/tmp/ccEsZppG.o:(.bss+0x0): multiple definition of `mailboxes'
/tmp/ccEZq43v.o:(.bss+0x0): first defined here
/tmp/ccEsZppG.o:(.bss+0xc0): multiple definition of `threads'
/tmp/ccEZq43v.o:(.bss+0xc0): first defined here
/tmp/ccEsZppG.o:(.bss+0x120): multiple definition of `semaphores'
/tmp/ccEZq43v.o:(.bss+0x120): first defined here
collect2: error: ld returned 1 exit status
Here is my code:
addem.cpp
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include "mailbox.h"
using namespace std;
void *sumUp(void *arg);
int main(int argc, char *argv[]) {
int numThreads, minThreads, maxInt, minInt;
if (argc < 3) {
cout << "Error: Need three arguments" << endl;
return 1;
}
numThreads = atoi(argv[1]);
maxInt = atoi(argv[2]);
minThreads = 1;
minInt = 1;
if (numThreads < 1) {
cout << "Cannot work with less than one thread\n"
<< "It's okay but do better next time!\n"
<< "We'll work with 1 thread this time.\n";
numThreads = minThreads;
} else if (numThreads > MAXTHREAD) {
cout << "Sorry, the max for threads is 10.\n"
<< "We'll work with 10 threads this time.\n";
numThreads = MAXTHREAD;
}
if (maxInt < 1) {
cout << "What do you want me to do? I can't count backwards!\n"
<< "I can barely count forwards! Let's make the max number\n"
<< "be 1 to save time\n";
maxInt = minInt;
}
struct msg outgoingMail[numThreads];
int divider = maxInt / numThreads;
int count = 1;
//initialize arrays (mailboxes, semaphores)
for (int i = 0; i < numThreads; i++) {
sem_init(&semaphores[i], 0, 1);
outgoingMail[i].iSender = 0;
outgoingMail[i].type = RANGE;
outgoingMail[i].value1 = count;
count = count + divider;
if (i = numThreads - 1) {
outgoingMail[i].value2 = maxInt;
} else {
outgoingMail[i].value2 = count;
}
}
for (int message = 0; message < numThreads; message++) {
SendMsg(message+1, outgoingMail[message]);
}
int thread;
for (thread = 0; thread <= numThreads; thread++) {
pthread_create(&threads[thread], NULL, &sumUp, (void *)(intptr_t)(thread+1));
}
struct msg incomingMsg;
int total = 0;
for (thread = 0; thread < numThreads; thread++) {
RecvMsg(0, incomingMsg);
total = total + incomingMsg.value1;
}
cout << "The total for 1 to " << maxInt << " using "
<< numThreads << " threads is " << total << endl;
return 0;
}
void *sumUp(void *arg) {
int index,total;
index = (intptr_t)arg;
struct msg message;
RecvMsg(index, message);
message.iSender = index;
message.type = ALLDONE;
total = 0;
for (int i = message.value1; i <= message.value2; i++) {
total += i;
}
SendMsg(0, message);
return (void *) 0;
}
mailbox.cpp
#include <stdio.h>
#include <iostream>
#include "mailbox.h"
using namespace std;
int SendMsg(int iTo, struct msg &Msg) {
if (safeToCall(iTo)) {
cout << "Error calling SendMsg" << endl;
return 1;
}
sem_wait(&semaphores[iTo]);
mailboxes[iTo] = Msg;
sem_post(&semaphores[iTo]);
return 0;
}
int RecvMsg(int iFrom, struct msg &Msg) {
sem_wait(&semaphores[iFrom]);
if (safeToCall(iFrom)) {
cout << "Error calling RecvMsg" << endl;
return 1;
}
mailboxes[iFrom] = Msg;
sem_post(&semaphores[iFrom]);
return 0;
}
bool safeToCall(int location) {
bool safe = !(location < 0 || location > MAXTHREAD + 1);
return safe;
//return true;
}
mailbox.h
#ifndef MAILBOX_H_
#define MAILBOX_H_
#define RANGE 1
#define ALLDONE 2
#define MAXTHREAD 10
#include <semaphore.h>
#include <pthread.h>
struct msg {
int iSender; /* sender of the message (0 .. numThreads)*/
int type; /* its type */
int value1; /* first value */
int value2; /* second value */
};
struct msg mailboxes[MAXTHREAD + 1];
pthread_t threads[MAXTHREAD + 1];
sem_t semaphores[MAXTHREAD + 1];
int SendMsg(int iTo, struct msg &Msg);
int RecvMsg(int iFrom, struct msg &Msg);
bool safeToCall(int location);
#endif
I am compiling the code with the command
g++ -o addem addem.cpp mailbox.cpp -lpthread
I have tried commenting out all of the function bodies in the source code to leave them as stub functions, and the same error occurs. The only way I have been able to compile the file is if I comment out the function bodies, and remove
#include "mailbox.h"
From at least one of the files. I feel it has to do with how I am initializing the arrays? But I cannot figure out a workaround.

c++ threading: cv.notify_one() blocks?

I wrote the following structure to implement a simple single producer / multi consumer synchronization. I'm using two integers available_index and consumed_index, access to consumed_index is protected by the condition variable cv. Here's the code:
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <thread>
struct ParserSync {
std::mutex worker_lock;
std::condition_variable cv;
int consumed_index = -1;
int available_index = -1;
bool exit_flag = false;
int consume_index() {
int ret = -1;
// get worker_lock
std::unique_lock<std::mutex> w_lock(worker_lock);
// wait for exit_flag or new available index
cv.wait(w_lock, [this] { return exit_flag || available_index > consumed_index; });
if (available_index > consumed_index) {
consumed_index++;
ret = consumed_index;
}
// Unlock mutex and notify another thread
w_lock.unlock();
cv.notify_one();
return ret;
}
void publish_index() {
available_index++;
std::cout << "before" << std::endl;
cv.notify_one();
std::cout << "after" << std::endl;
}
void set_exit() {
exit_flag = true;
cv.notify_all();
}
};
I tested my implementation using the following code (just a simple example to show the problem):
void producer(ParserSync &ps){
for (int i=0;i<5000;i++){
ps.publish_index();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ps.set_exit();
std::cout << "Producer finished!" << std::endl;
}
void consumer(ParserSync &ps){
while (true){
int idx = ps.consume_index();
if (idx == -1)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(4));
}
std::cout << "Consumer finished!" << std::endl;
}
int main() {
ParserSync ps{};
const int num_consumers = 4;
std::vector<std::thread> consumer_threads(num_consumers);
// start consumers
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i] = std::thread{consumer, std::ref(ps)};
}
// start producer
std::thread producer_thread = std::thread{producer, std::ref(ps)};
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i].join();
}
producer_thread.join();
std::cout << "Program finished" << std::endl;
return 0;
}
I would expect that producer thread produces 5000 indices and exits afterwards, but unfortunately, it gets stuck at some random iteration. I used print statements to find the code line that blocks and tracked it down to cv.notify_one();. This is the (shortened) console output:
...
before
after
before
after
before
Does anyone know why the call to cv.notify_one(); blocks?
I'm using MinGW (x86_64-6.2.0-posix-seh-rt_v5-rev1) on Windows 10.
Thanks in advance!
EDIT:
When compiling the exact same code with Visual Studio, the program works as expected and doesn't lock itself up. Unfortunately, I need to use MinGW for other reasons.

System V Message Queue Trouble

I am having a bit of trouble getting a System V Message Queue setup and working properly on Linux. The idea is to get a central node to pull data from several other nodes. The trouble is that the central node ends up sitting there waiting for the other nodes to send messages. I have looked at the values for the mailboxes and they are the same across all processes. I.E. 0 for the central mailbox, 32769 for other process 1, ect. I have no idea on why it appears to fail. I have tried to change the priority parameter in msgrcv to 0 to accept all incoming messages and the same issue occurs. Any help would be much appriciated. (Sorry for the lack of comments.)
Here is the code for the central node:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <iostream>
struct{
long priority;
int temperature;
int pid;
int stable;
} msgp;
const int mainMailID = 8484;
using namespace std;
int main(int argc, char* argv[]){
//declare needed variables
int centralMailID;
int externalMailID[4];
int tempdata;
int externalTempature[4];
int externalTemperatureLast[4];
//set initial values for msgp
msgp.priority = 2;
msgp.temperature = atoi(argv[1]);
msgp.pid = 0;
msgp.stable = 0;
//create the central mailbox
centralMailID = msgget(mainMailID, 0600 | IPC_CREAT);
if(centralMailID == -1){
cout << "Message Queue Creation Failed" << endl;
}
else{
cout << "Message Queue Created" << endl;
}
//create the external mailboxes
for(int i = 0; i < 4 ; i++){
externalMailID[i] = msgget(mainMailID + i+1, 0600 | IPC_CREAT);
if(externalMailID[i] == -1){
cout << "Message Queue " << i << " Creation Failed" << endl;
}
else{
cout << "Message Queue " << i << " Created" << endl;
}
}
printf("%i", externalMailID[0]);
while(msgp.stable == 0){
int centralTemperature = msgp.temperature;
//get the tempatures from the external sensors.
for(int i = 0; i<4; i++){
tempdata = msgrcv(externalMailID[i], &msgp, sizeof(msgp)-sizeof(long), 2, 0);
cout << "Recived data from sensor " << msgp.pid << endl;
externalTempature[i] = msgp.temperature;
}
if(externalTempature[0] == externalTempature[1] == externalTempature[2] == externalTempature[3] == centralTemperature){
msgp.stable = 1;
continue; //could also use break
}
int sum = 0;
for(int i = 0; i<4; i++){
sum = sum + externalTempature[i];
}
centralTemperature = ((2 * centralTemperature) + sum)/6;
msgp.temperature = centralTemperature;
for(int i = 0; i<4; i++){
tempdata = msgsnd(externalMailID[i], &msgp, sizeof(msgp)-sizeof(long), 0);
printf("Sent data to external mailbox %i", i);
}
}
printf("Process ended");
return 0;
}
Here is the code for the other nodes:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <iostream>
struct{
long priority;
int temperature;
int pid;
int stable;
} msgp;
const int mainMailID = 8484;
using namespace std;
int main(int argc, char* argv[]){
int centralMailID = msgget(mainMailID, 0600 | IPC_CREAT);
int pid = atoi(argv[2]);
int externalMailID = msgget(mainMailID + pid, 0600 | IPC_CREAT);
int externalTemperature = atoi(argv[1]);
int tempdata;
cout << externalMailID << endl;
msgp.priority = 2;
msgp.pid = pid;
msgp.stable = 0;
while(msgp.stable == 0){
msgp.temperature = externalTemperature;
tempdata = msgsnd(centralMailID, &msgp, sizeof(msgp)-sizeof(long), 0);
tempdata = msgrcv(externalMailID, &msgp, sizeof(msgp)-sizeof(long), 2, 0);
externalTemperature = ((externalTemperature * 3) + (msgp.temperature * 2))/5;
if(msgp.stable == 1){
continue;
}
}
printf("Child Process Ended");
return 0;
}
You're using the system V api, which is probably not what you want. See here for more details:
http://mij.oltrelinux.com/devel/unixprg/#ipc__posix_msgqs
The msgget, msgctl, msgsnd, msgrcv commands are part of the older, system V api, and while the semantics are similar, are not posix queues. A couple of quick google search for system V queue tutorials/examples are likely to solve your problem.
If you're genuinely looking to use posix queues, switch to and look for documentation on the mq_open, mq_close, mq_unlink, mq_send, mq_receive, mq_getattr, mq_setattr api.