Writers Preference with Semaphores in C++ - c++

I'm trying to write a writer's preference code to prevent a writer from being starved in the event that it's in a queue and readers skip it due to their priority. The counter checking how many readers have read is protected by a semaphore (readerCount), a try semaphore is used to indicate a reader is trying to enter (psembufT), and the resource semaphore (psembufF).
I need to write to a text file (code written) in one Terminal window and read from the other in another window, whenever I try to read i get Segmentation fault [core dumped] error.
#include <iostream>
#include <fstream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <inttypes.h>
using namespace std;
#define SHM_KEY 9876
#define SEMKEY 1234
struct sembuf vsembufR, psembufR, vsembufW, psembufW;
struct sembuf vsembufF, psembufF, vsembufT, psembufT;
int main()
{
union semun{
int val;
struct semid_ds *buf;
ushort myArray[0];
} arg;
string op;
ifstream myFile; // makes an ifstream object to read from myFile
int shmid = shmget(SHM_KEY, 256, 0777|IPC_CREAT);
int *readerCount = (int*)shmat(shmid, 0, 0);
int semid = semget(SEMKEY, 2, 0777|IPC_CREAT); // Creates two semaphores
int pause;
readerCount = 0;
psembufR.sem_num=0; // init reader mutex members
psembufR.sem_op=-1;
psembufR.sem_flg=SEM_UNDO;
vsembufR.sem_num=0;
vsembufR.sem_op=1;
vsembufR.sem_flg=SEM_UNDO;
psembufF.sem_num=1; // resource
psembufF.sem_op=-1;
psembufF.sem_flg=SEM_UNDO;
vsembufF.sem_num=1;
vsembufF.sem_op=1;
vsembufF.sem_flg=SEM_UNDO;
psembufW.sem_num=0; // writer
psembufW.sem_op=-1;
psembufW.sem_flg=SEM_UNDO;
vsembufW.sem_num=0;
vsembufW.sem_op=1;
vsembufW.sem_flg=SEM_UNDO;
psembufT.sem_num=1;
psembufT.sem_op=-1;
psembufT.sem_flg=SEM_UNDO;
vsembufT.sem_num=1;
vsembufT.sem_op=1;
vsembufT.sem_flg=SEM_UNDO;
arg.val = 1;
semctl(semid, 0, SETVAL, arg);
semctl(semid, 1, SETVAL, arg);
while(1){
cout << "Reader1:\n";
pause = getchar();
semop(semid, &psembufT, 1);
semop(semid, &psembufR, 1);
cout << "count inc" << endl;
*readerCount++;
if(*readerCount == 1) // is this first reader
semop(semid, &psembufF, 1); // lok resource from writers if 1st reader
semop(semid, &vsembufR, 1); // unlock reader mutex (for other readers)
semop(semid, &vsembufT, 1); // unlock try mutex (done accessing file)
// Critical Section
myFile.open ("myFile.txt", ios::out | ios::app); // ::app appends the myFile (new line)
if(myFile.is_open()){
while(getline(myFile, op)){
cout << op << endl; // reads
}
myFile.close();
}
semop(semid, &psembufR, 1); // lock reader mutex (avoid race)
*readerCount--;
if(*readerCount == 0) // is this the last reader
semop(semid, &vsembufF, 1); // unlock resource
semop(semid, &vsembufR, 1); // unlock reader mutex
}
I think it's something to do with how I declared readerCount but I can't figure it out.

readerCount = 0; should be *readerCount = 0;
readerCount is an int* so when you do readerCount = 0; you set that pointer to point at address zero. When you later try to update the value at that address, you are most likely to get crashes.

Related

How to properly wait for condition variable in C++?

In trying to create an asynchronous I/O file reader in C++ under Linux. The example I have has two buffers. The first read blocks. Then, for each time around the main loop, I asynchronously launch the IO and call process() which runs the simulated processing of the current block. When processing is done, we wait for the condition variable. The idea is that the asynchronous handler should notify the condition variable.
Unfortunately the notify seems to happen before wait, and it seems like this is not the way the condition variable wait() function works. How should I rewrite the code so that the loop waits until the asynchronous io has completed?
#include <aio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <condition_variable>
#include <cstring>
#include <iostream>
#include <thread>
using namespace std;
using namespace std::chrono_literals;
constexpr uint32_t blockSize = 512;
mutex readMutex;
condition_variable cv;
int fh;
int bytesRead;
void process(char* buf, uint32_t bytesRead) {
cout << "processing..." << endl;
usleep(100000);
}
void aio_completion_handler(sigval_t sigval) {
struct aiocb* req = (struct aiocb*)sigval.sival_ptr;
// check whether asynch operation is complete
if (aio_error(req) == 0) {
int ret = aio_return(req);
bytesRead = req->aio_nbytes;
cout << "ret == " << ret << endl;
cout << (char*)req->aio_buf << endl;
}
{
unique_lock<mutex> readLock(readMutex);
cv.notify_one();
}
}
void thready() {
char* buf1 = new char[blockSize];
char* buf2 = new char[blockSize];
aiocb cb;
char* processbuf = buf1;
char* readbuf = buf2;
fh = open("smallfile.dat", O_RDONLY);
if (fh < 0) {
throw std::runtime_error("cannot open file!");
}
memset(&cb, 0, sizeof(aiocb));
cb.aio_fildes = fh;
cb.aio_nbytes = blockSize;
cb.aio_offset = 0;
// Fill in callback information
/*
Using SIGEV_THREAD to request a thread callback function as a notification
method
*/
cb.aio_sigevent.sigev_notify_attributes = nullptr;
cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
cb.aio_sigevent.sigev_notify_function = aio_completion_handler;
/*
The context to be transmitted is loaded into the handler (in this case, a
reference to the aiocb request itself). In this handler, we simply refer to
the arrived sigval pointer and use the AIO function to verify that the request
has been completed.
*/
cb.aio_sigevent.sigev_value.sival_ptr = &cb;
int currentBytesRead = read(fh, buf1, blockSize); // read the 1st block
while (true) {
cb.aio_buf = readbuf;
aio_read(&cb); // each next block is read asynchronously
process(processbuf, currentBytesRead); // process while waiting
{
unique_lock<mutex> readLock(readMutex);
cv.wait(readLock);
}
currentBytesRead = bytesRead; // make local copy of global modified by the asynch code
if (currentBytesRead < blockSize) {
break; // last time, get out
}
cout << "back from wait" << endl;
swap(processbuf, readbuf); // switch to other buffer for next time
currentBytesRead = bytesRead; // create local copy
}
delete[] buf1;
delete[] buf2;
}
int main() {
try {
thready();
} catch (std::exception& e) {
cerr << e.what() << '\n';
}
return 0;
}
A condition varible should generally be used for
waiting until it is possible that the predicate (for example a shared variable) has changed, and
notifying waiting threads that the predicate may have changed, so that waiting threads should check the predicate again.
However, you seem to be attempting to use the state of the condition variable itself as the predicate. This is not how condition variables are supposed to be used and may lead to race conditions such as those described in your question. Another reason to always check the predicate is that spurious wakeups are possible with condition variables.
In your case, it would probably be appropriate to create a shared variable
bool operation_completed = false;
and use that variable as the predicate for the condition variable. Access to that variable should always be controlled by the mutex.
You can then change the lines
{
unique_lock<mutex> readLock(readMutex);
cv.notify_one();
}
to
{
unique_lock<mutex> readLock(readMutex);
operation_completed = true;
cv.notify_one();
}
and change the lines
{
unique_lock<mutex> readLock(readMutex);
cv.wait(readLock);
}
to:
{
unique_lock<mutex> readLock(readMutex);
while ( !operation_completed )
cv.wait(readLock);
}
Instead of
while ( !operation_completed )
cv.wait(readLock);
you can also write
cv.wait( readLock, []{ return operation_completed; } );
which is equivalent. See the documentation of std::condition_varible::wait for further information.
Of course, operation_completed should also be set back to false when appropriate, while the mutex is locked.

c++ asynchronous I/O in linux that waits on condition_variable, not waiting. What are we doing wrong?

In a previous questionTrying to write asynchronous I/O in C++ using locks and condition variables. This code calls terminate on the first lock() why?
,
we tried to use two mutexes to have asynchronous code that reads one block of a file into memory, then asynchronously tries to read the next block while processing the current one. Someone made a comment that using read was not the best way to do that. This is an attempt to use POSIX aio_read, but we are trying to wait on a condition_variable and do a notify on the condition variable in the callback after the I/O completes, and it's not working -- in the debugger we can see it blows right past the wait.
#include <aio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <condition_variable>
#include <cstring>
#include <iostream>
#include <thread>
using namespace std;
using namespace std::chrono_literals;
constexpr uint32_t blockSize = 512;
mutex readMutex;
mutex procMutex;
condition_variable cv;
int fh;
int bytesRead;
void process(char* buf, uint32_t bytesRead) {
cout << "processing..." << endl;
usleep(100000);
}
void aio_completion_handler(sigval_t sigval) {
struct aiocb* req = (struct aiocb*)sigval.sival_ptr;
// check whether asynch operation is complete
if (aio_error(req) == 0) {
int ret = aio_return(req);
cout << "ret == " << ret << endl;
cout << (char*)req->aio_buf << endl;
}
cv.notify_one();
}
void thready() {
char* buf1 = new char[blockSize];
char* buf2 = new char[blockSize];
aiocb cb;
char* processbuf = buf1;
char* readbuf = buf2;
fh = open("smallfile.dat", O_RDONLY);
if (fh < 0) {
throw std::runtime_error("cannot open file!");
}
memset(&cb, 0, sizeof(aiocb));
cb.aio_fildes = fh;
cb.aio_nbytes = blockSize;
cb.aio_offset = 0;
// Fill in callback information
/*
Using SIGEV_THREAD to request a thread callback function as a notification
method
*/
cb.aio_sigevent.sigev_notify_attributes = nullptr;
cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
cb.aio_sigevent.sigev_notify_function = aio_completion_handler;
/*
The context to be transmitted is loaded into the handler (in this case, a
reference to the aiocb request itself). In this handler, we simply refer to
the arrived sigval pointer and use the AIO function to verify that the request
has been completed.
*/
cb.aio_sigevent.sigev_value.sival_ptr = &cb;
int currentBytesRead = read(fh, buf1, blockSize); // read the 1st block
unique_lock<mutex> readLock(readMutex);
while (true) {
cb.aio_buf = readbuf;
aio_read(&cb); // each next block is read asynchronously
process(processbuf, currentBytesRead); // process while waiting
cv.wait(readLock);
if (currentBytesRead < blockSize) {
break; // last time, get out
}
cout << "back from wait" << endl;
swap(processbuf, readbuf); // switch to other buffer for next time
currentBytesRead = bytesRead; // create local copy
}
delete[] buf1;
delete[] buf2;
}
int main() {
try {
thready();
} catch (std::exception& e) {
cerr << e.what() << '\n';
}
return 0;
}

How to selectively ignore some inotify events?

Lets say I have two processes (simulated in this example with two threads) in a producer-consumer set up. That is, one process writes data to a file, the other process consumes the data in the file, then clears said file.
The set up I currently have, based on bits and pieces I've thrown together from various resources online, is that I should use a lock file to ensure that only one process can access the data file at a time. The producer acquires the lock, writes to the file, then releases the lock. Meanwhile, the consumer waits for modify events with inotify at which point it acquires the lock, consumes the data, and empties the file.
This seems relatively straightforward, but the part that's tripping me up is that when I empty the file out in my consumer thread, it triggers inotify modify event again, which sets off the whole flow again, and ends with the data file being cleared again, thus repeating forever.
I've tried a few ways to work around this problem, but none of them seem quite right. I'm worried doing this wrong will introduce potential race conditions or I'll end up skipping modify events or something.
Here is my current code:
#include <fstream>
#include <iostream>
#include <string>
#include "pthread.h"
#include "sys/file.h"
#include "sys/inotify.h"
#include "sys/stat.h"
#include "unistd.h"
const char* lock_filename = "./test_lock_file";
const char* data_filename = "./test_data_file";
int AquireLock(char const* lockName) {
mode_t m = umask(0);
int fd = open(lockName, O_RDWR | O_CREAT, 0666);
umask(m);
bool success = false;
if (fd < 0 || flock(fd, LOCK_EX) < 0) {
close(fd);
return -1;
}
return fd;
}
void ReleaseLock(int fd, char const* lockName) {
if (fd < 0) return;
remove(lockName);
close(fd);
}
void* ConsumerThread(void*) {
// Set up inotify.
int file_descriptor = inotify_init();
if (file_descriptor < 0) return nullptr;
int watch_descriptor =
inotify_add_watch(file_descriptor, data_filename, IN_MODIFY);
if (watch_descriptor < 0) return nullptr;
char buf[4096] __attribute__((aligned(__alignof__(inotify_event))));
while (true) {
// Read new events.
const inotify_event* event;
ssize_t numRead = read(file_descriptor, buf, sizeof(buf));
if (numRead <= 0) return nullptr;
// For each event, do stuff.
for (int i = 0; i < numRead; i += sizeof(inotify_event) + event->len) {
event = reinterpret_cast<inotify_event*>(&buf[i]);
// Critical section!
int fd = AquireLock(lock_filename);
// Read from the file.
std::string line;
std::ifstream data_file(data_filename);
if (data_file.is_open()) {
while (getline(data_file, line)) {
std::cout << line << std::endl;
}
data_file.close();
// Clear the file by opening then closing without writing to it.
std::ofstream erase_data_file(data_filename);
erase_data_file.close();
std::cout << "file cleared." << std::endl;
}
ReleaseLock(fd, lock_filename);
// Critical section over!
}
}
return nullptr;
}
int main(int argv, char** argc) {
// Set up other thread.
pthread_t thread;
int rc = pthread_create(&thread, NULL, ConsumerThread, nullptr);
if (rc) return rc;
// Producer thread: Periodically write to a file.
while (true) {
sleep(3);
// Critical section!
int fd = AquireLock(lock_filename);
// Write some text to a file
std::ofstream data_file(data_filename);
int counter = 0;
if (data_file.is_open()) {
std::cout << "Writing to file.\n";
data_file << "This is some example data. " << counter++ << "\n";
data_file.close();
}
ReleaseLock(fd, lock_filename);
// Critical section over!
}
pthread_exit(NULL);
return 0;
}
One idea I had was to disable tracking of modify events at the start of the consumer thread's critical section with inotify_rm_watch, then re-add it right before leaving the critical section. This doesn't seem to work though. Even with the events disabled, modify events are still getting triggered and I'm not sure why.
I've also considered just using a boolean to see if there was any file contents while consuming the file, and only clearing the file if it wasn't empty. This felt kind of hacky since it's still doing a second unnecessary iteration of the loop, but if I can't find a better solution I might just go with that. Ideally there would be a way to have only the producer thread's modifications trigger events, while the consumer could have it's own file modifications somehow ignored or disabled, but I'm not sure how to achieve that effect.

Checking whether a file is being written to with std::fstream

I am writing to, and reading from, a file called "results.dat", using std::fstream. This is done asynchronously, with the "read" and "write" function running in entirely different C++ programs. I want to check that the file is not currently being read before I write to it.
Suppose that my data is an array of 10 floats, then my "write" function looks like:
std::ofstream file_out("results.dat", std::ios::binary | std::ios::trunc);
file_out.write((char*)&data, 10 * sizeof(float));
file_out.close();
And my "read" function looks like:
std::ifstream file_in("results.dat", std::ios::binary);
file_in.read((char*)&data, 10 * sizeof(float));
file_in.close();
If the file "results.dat" is currently being read, I then want to make my "write" function wait, in a loop, until it is no longer being read. How can I implement this?
Use the LockFile() function on Windows or flock() for Linux/Unix.
For synchronous cases std::ios::tie.
The tie() is used to ensure that output from a tied stream appears before an input from the stream to
which it is tied. For example, cout is tied to cin:
cout << "Please enter a number: ";
int num;
cin >> num;
This code does not explicitly call cout.flush(), so had cout not been tied to cin, the user would see the request for input.
Here is another example:
#include <iostream> // std::ostream, std::cout, std::cin
#include <fstream> // std::ofstream
int main () {
std::ostream *prevstr;
std::ofstream ofs;
ofs.open ("test.txt");
std::cout << "tie example:\n";
*std::cin.tie() << "This is inserted into cout";
prevstr = std::cin.tie (&ofs);
*std::cin.tie() << "This is inserted into the file";
std::cin.tie (prevstr);
ofs.close();
return 0;
}
Further reading here.
For asynchronous cases using flock():
Example:
The lock file creation must be atomic. The pre-standard <fstream.h> library used to have an ios::noshare flag that guaranteed atomic file creation. Sadly, it was removed from the library, which superseded <fstream.h>. As a result, we are forced to use the traditional Unix file I/O interface declared in <fcntl.h> (under Unix and Linux) or <io.h> (Windows) to ensure an atomic operation.
Before a process can write to the data file, it should obtain a lock like this:
#include <fcntl.h> // for open()
#include <cerrno> // for errno
#include <cstdio> // for perror()
int fd;
fd=open("password.lck", O_WRONLY | O_CREAT | O_EXCL)
If the open() call succeeds, it returns a descriptor, which is a small positive integer that identifies the file. Otherwise, it returns -1 and assigns a matching error code to the global variable errno. The O_CREAT flag indicates that if the file doesn't exist, open() should create it. The O_EXCL flag ensures that the call is atomic; if the file already exists, open() will fail and set errno to EEXIST. This way you guarantee that only a single process at a time can hold the lock.
You check the return code of open() as follows:
int getlock() // returns the lock's descriptor on success
{
if (fd<0 && errno==EEXIST){
// the file already exist; another process is
// holding the lock
cout<<"the file is currently locked; try again later";
return -1;
}
else if (fd < 0){
// perror() appends a verbal description of the current
// errno value after the user-supplied string
perror("locking failed for the following reason");
return -1;
}
// if we got here, we own the lock
return fd;
}
Once a process owns the lock, it can write to the data file safely. When it has finished updating the file, it should delete the lock as follows:
remove("password.lck");
At this moment, the data file is considered unlocked and another process may access it.
or using: pthread_rwlockattr
#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include "check.h"
pthread_rwlock_t rwlock;
void *rdlockThread(void *arg)
{
int rc;
printf("Entered thread, getting read lock\n");
rc = pthread_rwlock_rdlock(&rwlock);
checkResults("pthread_rwlock_rdlock()\n", rc);
printf("got the rwlock read lock\n");
sleep(5);
printf("unlock the read lock\n");
rc = pthread_rwlock_unlock(&rwlock);
checkResults("pthread_rwlock_unlock()\n", rc);
printf("Secondary thread unlocked\n");
return NULL;
}
void *wrlockThread(void *arg)
{
int rc;
printf("Entered thread, getting write lock\n");
rc = pthread_rwlock_wrlock(&rwlock);
checkResults("pthread_rwlock_wrlock()\n", rc);
printf("Got the rwlock write lock, now unlock\n");
rc = pthread_rwlock_unlock(&rwlock);
checkResults("pthread_rwlock_unlock()\n", rc);
printf("Secondary thread unlocked\n");
return NULL;
}
int main(int argc, char **argv)
{
int rc=0;
pthread_t thread, thread1;
printf("Enter Testcase - %s\n", argv[0]);
printf("Main, initialize the read write lock\n");
rc = pthread_rwlock_init(&rwlock, NULL);
checkResults("pthread_rwlock_init()\n", rc);
printf("Main, grab a read lock\n");
rc = pthread_rwlock_rdlock(&rwlock);
checkResults("pthread_rwlock_rdlock()\n",rc);
printf("Main, grab the same read lock again\n");
rc = pthread_rwlock_rdlock(&rwlock);
checkResults("pthread_rwlock_rdlock() second\n", rc);
printf("Main, create the read lock thread\n");
rc = pthread_create(&thread, NULL, rdlockThread, NULL);
checkResults("pthread_create\n", rc);
printf("Main - unlock the first read lock\n");
rc = pthread_rwlock_unlock(&rwlock);
checkResults("pthread_rwlock_unlock()\n", rc);
printf("Main, create the write lock thread\n");
rc = pthread_create(&thread1, NULL, wrlockThread, NULL);
checkResults("pthread_create\n", rc);
sleep(5);
printf("Main - unlock the second read lock\n");
rc = pthread_rwlock_unlock(&rwlock);
checkResults("pthread_rwlock_unlock()\n", rc);
printf("Main, wait for the threads\n");
rc = pthread_join(thread, NULL);
checkResults("pthread_join\n", rc);
rc = pthread_join(thread1, NULL);
checkResults("pthread_join\n", rc);
rc = pthread_rwlock_destroy(&rwlock);
checkResults("pthread_rwlock_destroy()\n", rc);
printf("Main completed\n");
return 0;
}
Here are few useful resources: 1, 2 and 3.
Linux/Unix? Use a process-shared pthread_rwlock.
If both processes are running on the same machine, then you can use an interprocess mutex. Only one process at a time is allowed to interact with the data file (the resource).
Interprocess communication is different on all OSs, but the boost library provides a uniform interface for all common systems.
The code would look something like this:
writer:
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
using namespace boost::interprocess;
named_mutex mutex(open_or_create, "data_file_access_mutex");
void write_function() {
scoped_lock<named_mutex> lock(mutex);
std::ofstream file_out("results.dat", std::ios::binary | std::ios::trunc);
file_out.write((char*)&data, 10 * sizeof(float));
file_out.close();
}
reader:
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
using namespace boost::interprocess;
named_mutex mutex(open_or_create, "data_file_access_mutex");
void read_function() {
scoped_lock<named_mutex> lock(mutex);
std::ifstream file_in("results.dat", std::ios::binary);
file_in.read((char*)&data, 10 * sizeof(float));
file_in.close();
}

fork exec and mmap issues

For the application I'm developing (under Linux, but I'm trying to maintain portability) I need to switch to shared memory for sharing data across different processes (and threads inside processes). There is a father process generating different children
I need for example to get every process able to increment a shared counter using a named semaphore.
In this case everything is ok:
#include <sys/mman.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define SEM_NAME "/mysem"
#define SM_NAME "tmp_sm.txt"
int main(){
int fd, nloop, counter_reset;
int *smo;
sem_t *mutex;
nloop = 100;
counter_reset = 1000;
if (fork() == 0) {
/* child */
/* create, initialize, and unlink semaphore */
mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1);
//sem_unlink(SEM_NAME);
/* open file, initialize to 0, map into memory */
fd = open(SM_NAME, O_RDWR | O_CREAT);
smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
/* INCREMENT */
for (int i = 0; i < nloop; i++) {
sem_wait(mutex);
cout << "child: " << (*smo)++ << endl;
if(*smo>=counter_reset){
(*smo)=0;
}
sem_post(mutex);
}
exit(0);
}
/* parent */
/* create, initialize, and unlink semaphore */
mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1);
sem_unlink(SEM_NAME);
/* open file, initialize to 0, map into memory */
fd = open(SM_NAME, O_RDWR | O_CREAT);
smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
/* INCREMENT */
for (int i = 0; i < nloop; i++) {
sem_wait(mutex);
cout << "parent: " << (*smo)++ << endl;
if(*smo>=counter_reset){
(*smo)=0;
}
sem_post(mutex);
}
exit(0);
}
So far so good: both semaphore and shared counter are ok (same address in memory) and increment and reset work fine.
The program fails simply by moving child source code into a new source file invoked by exec. Shared memory and named semaphore addresses are different therefore increment fails.
Any suggestion? I used named semaphores and named shared memory (using a file) to try to get the same pointer values.
UPDATE:
as requested by Joachim Pileborg, this is the "server side" improvements respect above original code:
...
if (fork() == 0) {
/* child */
/*spawn child by execl*/
char cmd[] = "/path_to_bin/client";
execl(cmd, cmd, (char *)0);
cerr << "error while istantiating new process" << endl;
exit(EXIT_FAILURE);
}
...
And this is the "client" source code:
#include <sys/mman.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
#define SEM_NAME "/mysem"
#define SM_NAME "tmp_ssm.txt"
int main(){
int nloop, counter_reset;
int *smo;
sem_t *mutex;
/* create, initialize, and unlink semaphore */
mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1);
//sem_unlink(SEM_NAME);
/* open file, initialize to 0, map into memory */
int fd = open(SM_NAME, O_RDWR | O_CREAT);
smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
nloop=100;
counter_reset=1000;
/* INCREMENT */
for (int i = 0; i < nloop; i++) {
sem_wait(mutex);
cout << "child: " << (*smo)++ << endl;
if(*smo>=counter_reset){
(*smo)=0;
}
sem_post(mutex);
}
exit(0);
}
executing this code cause the process to block (deadlock) and waiting for an infinite time. looking at addresses they are tipically found to be:
father semaphore: 0x7f2fe1813000
child semahpore: 0x7f0c4c793000
father shared memory: 0x7f2fe1811000
child shared memory: 0x7ffd175cb000
removing 'sem_post' and 'sem_wait' everything is fine but I need mutual exlusion while incrementing...
Don't unlink the semaphore. it actually removes the semaphore.
From the sem_unlink manual page:
sem_unlink() removes the named semaphore referred to by name. The semaphore name is removed immediately. The semaphore is destroyed once all other processes that have the semaphore open close it.
This means that once you've created the semaphore in the parent process, you immediately remove it. The child process then will not be able to find the semaphore, and instead creates a new one.