ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I'm trying to create a semaphore shared amongst multiple processes for testing purposes. I want to see if I can create something similar to Named Mutexes and Named Events that are available in Windows.
I created a small class CNamedSemaphore. The class exposes 2 methods, Lock and Unlock. Lock decreases the semaphore count and Unlock increases it. If 2 processes try to Lock the same shared semaphore, the second one has to wait until the first one unlocks it and so on. Everything works well. If I use IPCS, I can see my shared semaphore.
The problem I'm facing is when the destructor of my class is called, it calls
semctl( m_shr_sem, 0, IPC_RMID );
The call destroys it completely. After running IPCS, I don't see it anymore even if I still have multiple processes running with a reference to that semaphore.
I assumed that there was some kind of reference count on it and only the last call would really destroy it but it is not the case. I looked inside the structures but I didn't see anything.
I can easily create shared memory to store my own count but it is an overhead I'd like to avoid if possible. Am I missing something? Is there a better way to do this.
Here is the code for my class:
Code:
class CNamedSemaphore
{
public:
CNamedSemaphore(long in_lValue, int *out_piReturn);
~CNamedSemaphore();
int CreateNamedSemaphore(long in_lValue);
int Lock();
int TryLock();
int Unlock();
private:
int m_shr_sem;
key_t m_semKey;
struct sembuf m_semBuf;
int m_flag;
};
#include "cnamedsemaphore.h"
#include <sys/mman.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 */
void *__pad;
};
CNamedSemaphore::CNamedSemaphore(long in_lValue, int *out_piReturn)
:
m_shr_sem(-1),
m_semKey(0)
{
*out_piReturn = CreateNamedSemaphore(in_lValue);
}
CNamedSemaphore::~CNamedSemaphore()
{
if (m_shr_sem < 0)
{
return;
}
semctl( m_shr_sem, 0, IPC_RMID );
}
int CNamedSemaphore::CreateNamedSemaphore(long in_lValue)
{
if (m_shr_sem != -1)
return -1;
m_semKey = (key_t) in_lValue;
m_flag = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
m_shr_sem = (int) semget( m_semKey, 1, m_flag );
if (m_shr_sem < 0)
{
m_flag |= IPC_CREAT;
m_shr_sem = (int) semget( m_semKey, 1, m_flag );
if (m_shr_sem < 0)
{
return -1;
}
}
if (m_flag & IPC_CREAT)
{
union semun arg;
arg.val = 1;
if (semctl(m_shr_sem, 0, SETVAL, arg) == -1)
{
return -1;
}
}
return 0;
}
int CNamedSemaphore::Lock()
{
if (m_shr_sem < 0)
{
return -1;
}
m_semBuf.sem_num = 0;
m_semBuf.sem_op = -1;
m_semBuf.sem_flg = SEM_UNDO;
if (semop(m_shr_sem, &m_semBuf, 1) != 0)
return -1;
return 0;
}
int CNamedSemaphore::TryLock()
{
if (m_shr_sem < 0)
{
return -1;
}
if (m_shr_sem < 0)
{
return -1;
}
int iReturn = 0;
m_semBuf.sem_num = 0;
m_semBuf.sem_op = -1;
m_semBuf.sem_flg = SEM_UNDO | IPC_NOWAIT;
errno = 0;
iReturn = semop(m_shr_sem, &m_semBuf, 1);
if (iReturn == -1)
{
if (errno == EAGAIN)
{
return -1;
}
}
return 0;
}
int CNamedSemaphore::Unlock()
{
if (m_shr_sem < 0)
{
return -1;
}
m_semBuf.sem_num = 0;
m_semBuf.sem_op = 1;
m_semBuf.sem_flg = SEM_UNDO;
if (semop(m_shr_sem, &m_semBuf, 1) != 0)
return -1;
return 0;
}
In the app, I would use
Code:
int iReturn;
pNamedSemaphore = (CNamedSemaphore *) new CNamedSemaphore(1000, &iReturn);
// Other parts of the code
/* Lock button was pressed */
pNamedSemaphore->Lock();
// Other parts of the code
/*Unlock button was pressed */
pNamedSemaphore->Unlock();
// Other parts of the code
/* Close button was pressed */
delete pNamedSemaphore;
In Windows, named semaphores (and synchronization objects in general) are reference-counted: the object remains until the last person using it cleans up.
SysV IPCs, however, are very different. If *anybody* deletes the object (given, of course, the privilege to delete it) - boom! It's gone.
You need to keep your own reference count on the semaphore. Frankly, I find it useful to create a second, "counting semaphore" to keep the reference count on my "working semaphore". Your suggestion about a shared global is another solution (although you're probably going to need to create a new semaphore anyway, just to protect the global variable).
I modified my class to create a semaphore set of 2 semaphores instead of only 1
semget(m_semKey, 2, flag);
It seems to be working... Cool!
Here's my modified class
Code:
#include "cnamedsemaphore.h"
#include <sys/mman.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 */
void *__pad;
};
CNamedSemaphore::CNamedSemaphore(long in_lValue, int *out_piReturn)
:
m_shr_sem(-1),
m_semKey(0)
{
*out_piReturn = CreateNamedSemaphore(in_lValue);
}
CNamedSemaphore::~CNamedSemaphore()
{
if (m_shr_sem < 0)
{
return;
}
union semun arg;
Lock();
arg.val = semctl(m_shr_sem, 1, GETVAL);
//check for > 0???
assert(arg.val);
// cout doesn't work well with threads
fprintf(stdout, "CNamedSemaphore::~CNamedSemaphore count=%d\n", arg.val);
arg.val--;
if (semctl(m_shr_sem, 1, SETVAL, arg) == -1)
{
arg.val = 0;
}
Unlock();
fprintf(stdout, "CNamedSemaphore::~CNamedSemaphore count=%d\n", arg.val);
if (arg.val <= 0)
{
semctl( m_shr_sem, 0, IPC_RMID );
}
}
int CNamedSemaphore::CreateNamedSemaphore(long in_lValue)
{
if (m_shr_sem != -1)
return -1;
int flag;
m_semKey = (key_t) in_lValue;
flag = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
m_shr_sem = (int) semget( m_semKey, 2, flag );
if (m_shr_sem < 0)
{
flag |= IPC_CREAT;
m_shr_sem = (int) semget( m_semKey, 2, flag );
if (m_shr_sem < 0)
{
return -1;
}
}
union semun arg;
if (flag & IPC_CREAT)
{
// could use SETALL?
arg.val = 1;
semctl(m_shr_sem, 1, SETVAL, arg);
semctl(m_shr_sem, 0, SETVAL, arg);
fprintf(stdout, "CNamedSemaphore::CreateNamedSemaphore IPC_CREAT count=%d\n", arg.val);
// for debugging purposes, check value
arg.val = semctl(m_shr_sem, 1, GETVAL);
fprintf(stdout, "CNamedSemaphore::CreateNamedSemaphore IPC_CREAT count=%d\n", arg.val);
}
else
{
Lock();
arg.val = semctl(m_shr_sem, 1, GETVAL);
arg.val++;
if (semctl(m_shr_sem, 1, SETVAL, arg) == -1)
{
Unlock();
return -1;
}
Unlock();
// for debugging purposes, check value
fprintf(stdout, "CNamedSemaphore::CreateNamedSemaphore count=%d\n", arg.val);
}
// for debugging purposes, check value
arg.val = semctl(m_shr_sem, 1, GETVAL);
fprintf(stdout, "CNamedSemaphore::CreateNamedSemaphore GETVAL count=%d\n", arg.val);
return 0;
}
int CNamedSemaphore::Lock()
{
if (m_shr_sem < 0)
{
return -1;
}
struct sembuf semBuf;
semBuf.sem_num = 0;
semBuf.sem_op = -1;
semBuf.sem_flg = SEM_UNDO;
if (semop(m_shr_sem, &semBuf, 1) != 0)
return -1;
return 0;
}
int CNamedSemaphore::TryLock()
{
if (m_shr_sem < 0)
{
return -1;
}
if (m_shr_sem < 0)
{
return -1;
}
struct sembuf semBuf;
semBuf.sem_num = 0;
semBuf.sem_op = -1;
semBuf.sem_flg = SEM_UNDO | IPC_NOWAIT;
errno = 0;
if (semop(m_shr_sem, &semBuf, 1) == -1)
{
if (errno == EAGAIN)
{
return -1;
}
// ??? Check errno!!!
assert(false);
}
return 0;
}
int CNamedSemaphore::Unlock()
{
if (m_shr_sem < 0)
{
return -1;
}
struct sembuf semBuf;
semBuf.sem_num = 0;
semBuf.sem_op = 1;
semBuf.sem_flg = SEM_UNDO;
if (semop(m_shr_sem, &semBuf, 1) != 0)
return -1;
return 0;
}
One thing about the shared memory that I didn't like is the fact that the minimum size is 4096 (getpagesize()). If at one point, I need to create many shared semaphores, one 4k block of shared memory per semaphore adds up quickly. I would have to implement my own shared memory manager.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.