Skip to content

Instantly share code, notes, and snippets.

@kgbook
Last active November 12, 2018 12:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kgbook/56ddc557e5b29743fd09899f9ee9628e to your computer and use it in GitHub Desktop.
Save kgbook/56ddc557e5b29743fd09899f9ee9628e to your computer and use it in GitHub Desktop.
[interprocess semaphore example]interprocess semaphore example #interprocess #semaphore
#ifndef SHARED_SEMAPHORE_H
#define SHARED_SEMAPHORE_H
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <semaphore.h>
#include <fcntl.h>
/* signal-safe semaphore */
class Semaphore {
public:
Semaphore(std::string fname, int value = 0) : semaphore_name(fname) {
unlink(semaphore_name);
if (!open(semaphore_name, value)) {
std::cerr <<__func__ <<", sem_open failed, " <<strerror(errno) <<std::endl;
}
ts.tv_sec = 2;
ts.tv_nsec = 0;
}
~Semaphore() {
if (!unlink(semaphore_name)) {
std::cerr <<__func__ <<", sem_unlink failed, " <<strerror(errno) <<std::endl;
}
if (!close()) {
std::cerr <<__func__ <<", close failed, " <<strerror(errno) <<std::endl;
}
}
/* use sigaction(2) SA_RESTART flag or check for EINTR on return of the wait
* to avoid the whole semaphore approach becomes error prone.
*/
bool wait() {
while (sem_wait(sem) < 0) {
/* The POSIX standard guarantees that it must behave as if it were a local variable for each thread,
* so we don’t have to worry about concurrent access to it.
*/
if (EINTR == errno) {
errno = 0;
} else {
std::cerr <<__func__ <<", sem_wait failed, " <<strerror(errno) <<std::endl;
return false;
}
}
return true;
}
bool try_wait() {
while (sem_trywait(sem) < 0) {
if (EINTR == errno) {
errno = 0;
} else {
std::cerr <<__func__ <<", sem_trywait failed, " <<strerror(errno) <<std::endl;
return false;
}
}
return true;
}
bool time_wait() {
while (sem_timedwait(sem, &ts) < 0) {
if (EINTR == errno) {
errno = 0;
} else {
std::cerr <<__func__ <<", sem_timedwait failed, " <<strerror(errno) <<std::endl;
return false;
}
}
return true;
}
bool post() {
if (sem_post(sem) < 0) {
std::cerr <<__func__ <<", sem_post failed, " <<strerror(errno) <<std::endl;
return false;
} else {
return true;
}
}
int32_t getValue() {
int32_t value;
if (sem_getvalue(sem, &value) < 0) {
std::cerr <<__func__ <<", sem_post failed, " <<strerror(errno) <<std::endl;
return -1;
}
return value;
}
private:
bool open(std::string fname, int32_t value) {
if (SEM_FAILED == (sem = sem_open(fname.c_str(), O_CREAT | O_EXCL, 0644, value))) {
return false;
}
return true;
}
bool close() {
if (sem_close(sem) < 0) {
return false;
}
return true;
}
bool unlink(std::string fname) {
if (sem_unlink(fname.c_str()) < 0) {
return false;
}
return true;
}
private:
std::string semaphore_name;
sem_t *sem;
struct timespec ts;
};
#endif //SHARED_SEMAPHORE_H
#include <csignal>
#include <sys/wait.h>
#include "SharedSemaphore.h"
pid_t parent_pid;
Semaphore *sem;
void parent_process_callback() {
std::cout <<__func__ <<", pid: " <<getpid() <<std::endl;
}
void child_process_callback() {
std::cout <<__func__ <<", pid: " <<getpid() <<std::endl;
}
static void sigint_handler(int32_t signo) {
std::cout <<__func__ <<", signo:" <<signo <<", pid:" <<getpid() <<std::endl;
if (parent_pid != getpid()) {
exit(0);
}
}
static void setup_sigint_handler() {
struct sigaction act;
act.sa_handler = sigint_handler;
sigemptyset(&act.sa_mask);
#if 0
act.sa_flags = 0;
#else
act.sa_flags = SA_RESTART; /* Interrupted system call when sem_wait */
#endif
sigaction(SIGINT, &act, NULL);
}
void reclaim_zombie_process() {
while (1) {
pid_t reclaim_pid = waitpid(child_pid, nullptr, 0);
if (reclaim_pid < 0) {
if (ECHILD == errno) {
std::cout <<__func__ <<", all child process terminated!" <<std::endl;
break;
} else {
std::cout <<__func__ <<", waitpid failed, " <<strerror(errno) <<std::endl;
continue;
}
}
std::cout <<__func__ <<", pid: " <<reclaim_pid <<" terminated!" <<std::endl;
}
}
int main(int argc, char **argv) {
sem_parent = new Semaphore("parent-process");
setup_sigint_handler();
pid_t pid = fork();
switch (pid) {
case 0: {
std::cout <<"child process, pid:" <<getpid() <<std::endl;
atexit(child_process_callback);
break;
}
case -1: {
std::cerr <<"fork failed, " <<strerror(errno) <<std::endl;
break;
}
default: {
parent_pid = getpid();
std::cout <<"parent process! pid:" <<parent_pid <<", child process id: " << pid <<std::endl;
atexit(parent_process_callback);
reclaim_zombie_process();
break;
}
}
std::cout <<"semaphore value: " <<sem->getValue() <<std::endl;
sem->wait();
std::cout <<"process " <<getpid() <<std::endl;
/* deinit here */
std::cout <<"deinit done!" <<std::endl;
delete sem;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment