Example

Two communicating processes

The example here shows two communicating processes that share a std::set. The first process fills the std::set and after it ends, the second iterates it through and lists imposed values to std::cout. Communication is synchronized using the standard mutex created via SYSV semaphores.

#include <mm.h>;
#include <cerrno>;
#include <cstdlib>
#include <iostream>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <shallocator/shset.h>
#include <shallocator/shmemory.h>
#include <shallocator/shstring.h>

#ifdef _SEM_SEMUN_UNDEFINED  // see sys/sem.h

union semun {
    int              val;    // value for SETVAL
    struct semid_ds *buf;    // buffer for IPC_STAT, IPC_SET
    unsigned short  *array;  // array for GETALL, SETALL
    struct seminfo  *__buf;  // buffer for IPC_INFO (Linux-specific)
};

#endif // _SEM_SEMUN_UNDEFINED

/** 
 * @short Holder for sem set id.
 */
class SemSetIdHolder_t {
public:
    SemSetIdHolder_t(int semsetid)
        : semsetid(semsetid), hold(true)
    {
        // check semaphore set id
        if (semsetid < 0) {
            fprintf(stderr, "semget: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    /** 
     * @short Deallocate semaphore set.
     */
    ~SemSetIdHolder_t() {
        if (hold) {
            if (semctl(semsetid, 0, IPC_RMID, 0)) {
                fprintf(stderr, "semctl: %s\n", strerror(errno));
                exit(EXIT_FAILURE);
            }
        }
    }

    operator int() const { return semsetid;}

    void release() { hold = false;}

private:
    int semsetid;
    bool hold;
};

/** 
 * @short IPC semaphore mutex.
 */
class Mutex_t {
public:
    /** 
     * @short Allocate new semaphore set.
     */
    Mutex_t()
        : semsetid(semget(IPC_PRIVATE, 1, IPC_CREAT | 0600))
    {
        // init semaphore
        union semun arg;
        arg.val = 1;
        if ((semctl(semsetid, 0, SETVAL, arg)) < 0) {
            fprintf(stderr, "semctl: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    void lock() {
        // prepare structure for lock
        struct sembuf op = { 0, -1, SEM_UNDO};

        // lock
        if (TEMP_FAILURE_RETRY(semop(semsetid, &op, 1)) < 0) {
            fprintf(stderr, "semop: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    void unlock() {
        // prepare structure for unlock
        struct sembuf op = { 0, +1, SEM_UNDO};

        // unlock
        if (TEMP_FAILURE_RETRY(semop(semsetid, &op, 1)) < 0) {
            fprintf(stderr, "semop: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    void release() { semsetid.release();}

private:
    Mutex_t(const Mutex_t &);
    Mutex_t &operator=(const Mutex_t &);

    SemSetIdHolder_t semsetid;
};

/** 
 * @short Scope locker.
 */
class Lock_t {
public:
    Lock_t(Mutex_t &mutex): mutex(mutex) { mutex.lock();}
    ~Lock_t() { mutex.unlock();}

private:
    Lock_t(const Lock_t &);
    Lock_t &operator=(const Lock_t &);
    Mutex_t &mutex;
};

/** 
 * @short Shared memmory pool holder.
 */
class SHPool_t {
public:
    SHPool_t(): hold(true) { MM_create(1024, 0);}
    ~SHPool_t() { if (hold) MM_destroy();}
    void release() { hold = false;}

private:
    SHPool_t(const SHPool_t &);
    SHPool_t &operator=(const SHPool_t &);
    bool hold;
};

/** 
 * @short Usage example.
 */
void process(SHAllocator::shauto_ptr
             <SHAllocator::shset<SHAllocator::shstring> > set,
             SHPool_t &pool, Mutex_t &mutex) {
    // if I'm first the set is empty
    if (set->empty()) {
        // prepare some value
        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "%d", getpid());

        // store value in set
        set->insert(buffer);

        // I shouldn't destroy set since other process is going to work with it
        set.release();
        pool.release();
        mutex.release();

    } else {
        // dump data to std::cout
        for (SHAllocator::shset<SHAllocator::shstring>::const_iterator
                iset = set->begin(), eset = set->end();
                iset != eset; ++iset) {
            std::cout << getpid() << ": " << *iset << std::endl;
        }
    }
}

/** 
 * @short The main.
 */
int main(int /*argc*/, char ** /*argv*/) {

    // allocate space for new allocator
    SHPool_t pool;

    // get new mutex
    Mutex_t mutex;

    // create new set container and store new pointer into auto_ptr
    SHAllocator::shauto_ptr<SHAllocator::shset<SHAllocator::shstring> > set
       (new (SHAllocator::SHAlloc) SHAllocator::shset<SHAllocator::shstring>());

    // do fork - don't care about errors ;-)
    if (!fork()) {
        // in child lock mutex first
        Lock_t lock(mutex);

        // try insert if child lock mutex first or dump data and destroy
        // all resources
        process(set, pool, mutex);

    } else {
        // in parent lock mutex first too
        Lock_t lock(mutex);

        // try insert if parent lock mutex first or dump data and destroy
        // all resources
        process(set, pool, mutex);
    }

    return EXIT_SUCCESS;
}