4696 lines
160 KiB
C++
4696 lines
160 KiB
C++
|
|
|
|
#ifndef __ZCPPMAIN__PROCESS_LINUX_H__
|
|
#define __ZCPPMAIN__PROCESS_LINUX_H__
|
|
|
|
|
|
#include "ZCppMain/ZMainHead.H"
|
|
|
|
#include <fstream>
|
|
#include <sys/time.h>
|
|
#include <signal.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/sem.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
#include <semaphore.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
|
|
|
|
namespace ZNsMain
|
|
{
|
|
|
|
/*////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 대부분의 쓰레드 함수는 성공하면 0 을 반환하는데 이 값을 enum 지정한다.
|
|
|
|
////////////////////////////////////////////////////////////////////////*/
|
|
|
|
namespace ZNsEnum
|
|
{
|
|
|
|
// 쓰레드 관련 함수가 에러일 경우에는 정책에 따라, ZEThread_Invalid 이외의 값을 반환할 수도 있다.
|
|
|
|
enum ZEThread
|
|
{
|
|
ZEThread_OK = 0 ,
|
|
ZEThread_Invalid= EINVAL , // 쓰레드 관련 함수 에러.
|
|
ZEThread_TimeOut= ETIMEDOUT
|
|
};/*
|
|
enum ZEThread*/
|
|
|
|
|
|
/*////////////////////////////////////////
|
|
|
|
■ barrier 의 대표적인 반환값.
|
|
|
|
■ barrier 에러를 체크할 때
|
|
|
|
BarrierClass VO_BarrierClass;
|
|
|
|
if(VO_BarrierClass.Init()==ZEBarrier_NO)
|
|
{
|
|
// some code
|
|
}
|
|
|
|
로 하지 말고.
|
|
|
|
if(VO_BarrierClass.Init()!=ZEBarrier_OK)
|
|
{
|
|
// some code
|
|
}
|
|
|
|
로 해야 안전하다.
|
|
|
|
////////////////////////////////////////*/
|
|
|
|
enum ZEBarrier
|
|
{
|
|
ZEBarrier_NO = -1,
|
|
ZEBarrier_OK = 0
|
|
};/*
|
|
enum ZEBarrier*/
|
|
|
|
}/*
|
|
namespace ZNsEnum*/
|
|
|
|
|
|
/*/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 리눅스에서 pthread 라이브러리에 링크를 걸어줄 것.
|
|
|
|
■ 한컴 베포판 3.0 의 경우 sys/sem.h 에는 다음과 같은 설명이 나와 있다.
|
|
|
|
The user should define a union like the following to use it for arguments
|
|
for `semctl'.
|
|
|
|
union semun
|
|
{
|
|
int val; <= value for SETVAL
|
|
struct semid_ds *buf; <= buffer for IPC_STAT & IPC_SET
|
|
unsigned short int *array; <= array for GETALL & SETALL
|
|
struct seminfo *__buf; <= buffer for IPC_INFO
|
|
};
|
|
|
|
Previous versions of this file used to define this union but this is
|
|
incorrect. One can test the macro _SEM_SEMUN_UNDEFINED to see whether
|
|
one must define the union or not.
|
|
|
|
#define _SEM_SEMUN_UNDEFINED 1
|
|
|
|
■ IPC object 의 플래그는 IPC_CREAT, IPC_EXCL, 0 의 세 값중의 하나이며, 0 이면 해당 동
|
|
기화 object 가 존재할 때만 연다.
|
|
|
|
<semaphore.h> 파일에 있는 세마포어 함수는 프로세스의 경우에는 지원되지 않고 있다.
|
|
그래서 프로세스의 경우에는 <sys/sem.h> 의 세마포어 함수를 써야 한다.
|
|
|
|
-- 2004-07-12
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
union semun
|
|
{
|
|
int val ; // value for SETVAL
|
|
struct semid_ds* buf ; // buffer for IPC_STAT & IPC_SET
|
|
unsigned short int* array; // array for GETALL & SETALL
|
|
struct seminfo* __buf ; // buffer for IPC_INFO
|
|
};/*
|
|
union semun*/
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////
|
|
|
|
■ 윈도우에서는 동기화 object 에 이름을 부여할 수 있는데, 리눅스에
|
|
서는 숫자키만 부여할 수 있다. 그래서 호환을 위하여 이름으로부터
|
|
키를 유추하는 함수를 만들었다. 파일 이름을 안다면 ftok() 함수
|
|
|
|
key_t ftok(const char *path, int id);
|
|
|
|
를 사용하는 것도 방법이다.
|
|
|
|
//////////////////////////////////////////////////////////////////*/
|
|
|
|
static inline long ZfGetKeyNum(const char* const AP_Name)
|
|
{
|
|
int VI_Index =-1 ;
|
|
long VL_KeyID = 1 ;
|
|
|
|
if(AP_Name!=0)
|
|
{
|
|
while(AP_Name[++VI_Index]!=0)
|
|
{
|
|
VL_KeyID += AP_Name[VI_Index]*(VI_Index+1) ;
|
|
}/*
|
|
while(AP_Name[++VI_Index]!=0)*/
|
|
}/*
|
|
if(AP_Name!=0)*/
|
|
|
|
return VL_KeyID;
|
|
}/*
|
|
static inline long ZfGetKeyNum(const char* const AP_Name)*/
|
|
|
|
|
|
/*/////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 시그널은 쌓이지 않는다. 즉 동일한 이벤트가 연이어서 다섯 번 발생했다고 해서
|
|
프로세서에게 동일한 시그널을 다섯 번 전달해 주지 않는다. 이러한 문제점은
|
|
sa_mask 를 통해 해결할 수 있다. sa_mask 에 설정된 시그널들은 동일한 이벤트가
|
|
연이이서 다섯번 발생하는 경우 순차적으로 시그널을 발생시킨다. 즉 첫번째 시그
|
|
널이 처리되는 동안 나머지 시그널들은 블로킹 상태에 있게 된다.
|
|
|
|
-- TCP/IP 소켓 프로그래밍 (윤성우 저) 245 Page
|
|
|
|
■ 중요 Ansi, Posix 시그널 정의
|
|
|
|
#define SIG_ERR ((__sighandler_t) -1) // Error return.
|
|
#define SIG_DFL ((__sighandler_t) 0) // Default action.
|
|
#define SIG_IGN ((__sighandler_t) 1) // Ignore signal.
|
|
|
|
#define SIGHUP 1 // Hangup (POSIX)
|
|
#define SIGINT 2 // Interrupt (ANSI)
|
|
#define SIGQUIT 3 // Quit (POSIX)
|
|
#define SIGILL 4 // Illegal instruction (ANSI)
|
|
#define SIGTRAP 5 // Trace trap (POSIX)
|
|
#define SIGABRT 6 // Abort (ANSI)
|
|
#define SIGFPE 8 // Floating-point exception (ANSI)
|
|
#define SIGKILL 9 // Kill, unblockable (POSIX)
|
|
#define SIGUSR1 10 // User-defined signal 1 (POSIX)
|
|
#define SIGSEGV 11 // Segmentation violation (ANSI)
|
|
#define SIGUSR2 12 // User-defined signal 2 (POSIX)
|
|
#define SIGPIPE 13 // Broken pipe (POSIX)
|
|
#define SIGALRM 14 // Alarm clock (POSIX)
|
|
#define SIGTERM 15 // Termination (ANSI)
|
|
#define SIGSTKFLT 16 // Stack fault
|
|
#define SIGCHLD 17 // Child status has changed (POSIX)
|
|
#define SIGCONT 18 // Continue (POSIX)
|
|
#define SIGSTOP 19 // Stop, unblockable (POSIX)
|
|
#define SIGTSTP 20 // Keyboard stop (POSIX)
|
|
#define SIGTTIN 21 // Background read from tty (POSIX)
|
|
#define SIGTTOU 22 // Background write to tty (POSIX)
|
|
#define SIGSYS 31 // Bad system call
|
|
#define SIGUNUSED 31
|
|
|
|
◇ SIGHUP
|
|
|
|
- 터미널 인터페이스에 의해 연결의 단절이 감지되면 해당 제어 터미널과 연결된 제어 프로세스(세션 리더)에게 전달
|
|
- 세션 리더가 종료했을 때도 발생하는데 이때는 foreground 프로세스 그룹내의 모든 프로세스들에게 전달
|
|
- SIGHUP 시그널은 원래 모뎀 연결 끊김 등의 시리얼 라인이 끊어지면 발생하는 시그널이다.
|
|
- 이름 있는 시스템 데몬들은 SIGHUP 시그널을 configure file을 다시 읽어들이는 등의 초기화 신호로 해석한다.
|
|
. bootp(8), gated(8), inetd(8), mountd(8), named(8), nfsd(8), ypbind(8)
|
|
. pppd(8) 처럼 SIGHUP을 원래의 의도에 충실하게 세션 종료의 뜻으로 받아들이는 사례도 간혹 있는데, 요새는 보편적으로 이 역할을 SIGTERM이 맡는다.
|
|
. daemon은 제어 단말기 없이 돌기 때문에 kernel로부터 SIGHUP 신호를 수신하지 못한다.
|
|
그러므로 많은 daemon은 이 신호를 daemon의 구성 파일이 변경되어 daemon이 그 파일을 새로 읽어야 된다는 것을 알려주는 관리자로부터의 통지로 사용한다.
|
|
. daemon이 수신할 수 없는 다른 두 개의 신호로 SIGINT와 SIGWINCH가 있고 이들도 역시 어떤 변화를 daemon에게 통지하기 위해 사용될 수 있다.
|
|
|
|
◇ SIGINT
|
|
|
|
- 인터럽트 키 (DELETE 또는 Control-C)가 눌렸을 때 발생
|
|
|
|
◇ SIGQUIT
|
|
|
|
- Control-backslash 에 의해 발생
|
|
|
|
◇ SIGCHLD
|
|
|
|
- 프로세스가 종료하거나 정지하면, 부모 프로세스에게 전달된다.
|
|
- 부모 프로세스는 wait() 시스템 콜을 사용하여 무슨 일이 일어났는지 알아본다.
|
|
- 이 시그널에 대한 default 처리는 무시하는 것이다. 즉 프로세스가 이 신호를 받으려고 할 때만 전달된다.
|
|
|
|
◇ SIGSEGV
|
|
|
|
- 유효하지 않은 가상 메모리 주소를 참조하거나 사용 권한이 없는 메모리에 접근할 때 프로세스로 전달된다.
|
|
|
|
◇ SIGTERM
|
|
|
|
- kill 명령에 의해 기본적으로 발생
|
|
|
|
◇ SIGKILL
|
|
|
|
- "극단의 조치(extreme prejudice)"로 프로그램을 종료하는 데 사용된다.
|
|
- 시그널 catch 하거나 무시할 수 없다.
|
|
|
|
◇ SIGALRM
|
|
|
|
- alarm()이나 setitimer() 시스템 콜로 설정한 알람 시간이 초과 했을 때 프로세스로 전달된다.
|
|
|
|
◇ SIGTSTP
|
|
|
|
- Control-Z 키에 의해 발생
|
|
- 기본 처리 방법은 SIGCONT 신호를 받을 때까지 프로세스를 중단한다.
|
|
|
|
◇ SIGCONT
|
|
|
|
- 정지한 프로세스를 계속 실행시키려 할 때 발생
|
|
- 이 신호는 받을 수 있지만 블록하거나 무시할 수 없다.
|
|
- 기본 처리 방법은 중단된 프로세스를 재시작하는 것이다. 그러나 프로세스가 신호를 받지 않는다면 신호를 버린다.
|
|
- vi 에디터를 사용할 때
|
|
. Control-Z 를 눌러 수행을 잠시 정지시키면 쉘이 키 입력을 처리하게 되는데
|
|
. 이때 fg 명령을 실행시키면 쉘은 vi 에게 SIGCONT 시그널을 전달하며
|
|
. vi는 이 시그널에 대한 처리로 화면을 다시 그리고 사용자 키 입력을 받는 상태로 돌아간다.
|
|
|
|
◇ SIGSTOP
|
|
|
|
- SIGTSTP과 동일하나 catch 하거나 무시할 수 없다.
|
|
- 이 신호를 받으면 무조건 SIGCONT 신호를 받을 때까지 프로세스를 중단한다.
|
|
|
|
◇ SIGABRT
|
|
|
|
- abort() 함수의 호출로 발생
|
|
|
|
◇ SIGBUS
|
|
|
|
- 하드웨어 결함으로 발생
|
|
|
|
◇ SIGEMT
|
|
|
|
- 하드웨어 결함으로 발생
|
|
|
|
◇ SIGFPE
|
|
|
|
- divide-by-0나 부동 소숫점 오버플로우와 같은 산술 연산 오류에서 발생
|
|
|
|
◇ SIGILL
|
|
|
|
◇ SIGINFO
|
|
|
|
◇ SIGIO
|
|
|
|
◇ SIGIOT
|
|
|
|
◇ SIGPIPE
|
|
|
|
- pipe 통신에서 수신 프로세스가 종료했을 때 송신 프로세스가 파이프에 write 하면 발생
|
|
- 프로세스가 RST를 받은 소켓에 데이터를 쓰면, 커널은 그 프로세스에 ISGPIPE 신호를 보낸다.
|
|
- 이 신호의 기본 동작은 프로세스를 종료시키는 것이므로, 프로세스가 원하지 않는 종료를 피하기 위해서는 이 신호를 포착해야 한다.
|
|
|
|
◇ SIGPOLL
|
|
|
|
◇ SIGROF
|
|
|
|
◇ SIGPWR
|
|
|
|
◇ SIGSYS
|
|
|
|
◇ SIGTTIN
|
|
|
|
- background에 있는 프로세스가 제어 터미널로부터의 읽기를 시도한다.
|
|
|
|
◇ SIGTTOU
|
|
|
|
- background에 있는 프로세스가 제어 터미널로부터의 쓰기를 시도한다.
|
|
|
|
◇ SIGURG
|
|
|
|
- SIGIO와 SIGURG 라는 두 개의 신호는 소켓이 F_SETOWN 명령으로 소유주에게 할당되었을 때만 소켓에 대해 발생한다.
|
|
|
|
◇ SIGUSR1
|
|
|
|
◇ SIGUSR2
|
|
|
|
◇ SIGVTALRM
|
|
|
|
◇ SIGWINCH
|
|
|
|
◇ SIGXCPU
|
|
|
|
◇ SIGXFSZ
|
|
|
|
■ -- 2009-11-29 00:26:00
|
|
|
|
■ linux 에서의 sigaction 정의 -- 2011-06-05 22:55:00
|
|
|
|
struct sigaction {
|
|
union {
|
|
__sighandler_t _sa_handler;
|
|
void (*_sa_sigaction)(int, struct siginfo *, void *);
|
|
} _u;
|
|
sigset_t sa_mask ;
|
|
unsigned long sa_flags;
|
|
void (*sa_restorer)(void);
|
|
};
|
|
|
|
#define sa_handler _u._sa_handler
|
|
#define sa_sigaction _u._sa_sigaction
|
|
|
|
■ solaris 에서의 sigaction 정의 -- 2011-06-05 22:56:00
|
|
|
|
struct sigaction {
|
|
int sa_flags;
|
|
union {
|
|
#ifdef __cplusplus
|
|
void (*_handler)(int);
|
|
#else
|
|
void (*_handler)();
|
|
#endif
|
|
#if defined(__EXTENSIONS__) || defined(_KERNEL) || \
|
|
(!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \
|
|
(_POSIX_C_SOURCE > 2) || defined(_XPG4_2)
|
|
void (*_sigaction)(int, siginfo_t *, void *);
|
|
#endif
|
|
} _funcptr;
|
|
sigset_t sa_mask;
|
|
#ifndef _LP64
|
|
int sa_resv[2];
|
|
#endif
|
|
};
|
|
|
|
#define sa_handler _funcptr._handler
|
|
#define sa_sigaction _funcptr._sigaction
|
|
|
|
■ freebsd 에서의 sigaction 정의 -- 2011-06-05 22:59:00
|
|
|
|
struct sigaction {
|
|
union { void (*__sa_handler)(int);
|
|
void (*__sa_sigaction)(int, struct __siginfo *, void *);
|
|
} __sigaction_u; // signal handler
|
|
int sa_flags; // see signal options below
|
|
sigset_t sa_mask; // signal mask to apply
|
|
};
|
|
|
|
#define sa_handler __sigaction_u.__sa_handler
|
|
|
|
/////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
class ZCSigAction : public sigaction
|
|
{
|
|
public:
|
|
typedef struct sigaction StSigAction;
|
|
public:
|
|
|
|
ZCSigAction()
|
|
{
|
|
::memset((sigaction*)this, 0, sizeof(StSigAction));
|
|
}/*
|
|
ZCSigAction()*/
|
|
|
|
StSigAction& GetBaseObj()
|
|
{
|
|
return (StSigAction&)(*this);
|
|
}/*
|
|
StSigAction& GetBaseObj()*/
|
|
|
|
void SetFlags(int AI_Flags)
|
|
{
|
|
this->sa_flags=AI_Flags; // RTS 에는 SA_SIGINFO 를 셋팅
|
|
}/*
|
|
void SetFlags(int AI_Flags)*/
|
|
|
|
void SetHandler(void (*APF_Handler)(int))
|
|
{
|
|
this->sa_handler=APF_Handler;
|
|
}/*
|
|
void SetHandler(void (*APF_Handler)(int))*/
|
|
|
|
void SetHandlerEx(void (*APF_Handler)(int, siginfo_t*, void*))
|
|
{
|
|
this->sa_sigaction=APF_Handler; // 주로 RTS 에서 사용됨
|
|
}/*
|
|
void SetHandlerEx(void (*APF_Handler)(int, siginfo_t*, void*))*/
|
|
|
|
bool EmptySet()
|
|
{
|
|
return sigemptyset(&this->sa_mask)==0 ;
|
|
}/*
|
|
bool EmptySet()*/
|
|
|
|
bool FillSet()
|
|
{
|
|
return sigfillset(&this->sa_mask)==0 ;
|
|
}/*
|
|
bool FillSet()*/
|
|
|
|
bool CutSet(int AI_SigNo)
|
|
{
|
|
return sigdelset(&this->sa_mask, AI_SigNo)==0 ;
|
|
}/*
|
|
bool CutSet(int AI_SigNo)*/
|
|
|
|
bool AddSet(int AI_SigNo)
|
|
{
|
|
return sigaddset(&this->sa_mask, AI_SigNo)==0 ;
|
|
}/*
|
|
bool AddSet(int AI_SigNo)*/
|
|
|
|
bool Act(int AI_SigNum, struct sigaction* AP_OldSigAction=0)
|
|
{
|
|
return ::sigaction(AI_SigNum, this, AP_OldSigAction)==0 ;
|
|
}/*
|
|
bool Act(int AI_SigNum, struct sigaction* AP_OldSigAction=0)*/
|
|
|
|
|
|
static ZTypUInt Alarm(ZTypUInt AI_Sec)
|
|
{
|
|
return ::alarm(AI_Sec);
|
|
|
|
/* AI_Sec 0 은 나머지 알람 요청을 취소. 나머지 알람 호출
|
|
이 전달되기 전에 남아있는 시간을 반환하거나 호출이
|
|
실패하면 -1 을 반환한다. */
|
|
}/*
|
|
static ZTypUInt Alarm(ZTypUInt AI_Sec)*/
|
|
|
|
static bool Kill(int AI_SigNo, pid_t AI_Pid=::getpid())
|
|
{
|
|
return ::kill(AI_Pid, AI_SigNo)==0 ;
|
|
}/*
|
|
static bool Kill(int AI_SigNo, pid_t AI_Pid=::getpid())*/
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
/*/////////////////////////////////////////////////////////////////////////
|
|
|
|
■ freebsd 8.2, solaris 5.11 에서는 F_SETSIG 이 정의가 되어 있지 않아서,
|
|
아래 SetupRTS() 함수를 사용할 수 없다. 즉 RTS 와 소켓을 연동할 수 없다.
|
|
|
|
-- 2011-06-09 04:25:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////*/
|
|
|
|
// 주로 SIGRTMIN ~ SIGRTMAX 사이에 있는 RTS 를 설정한다.
|
|
|
|
static bool SetupRTS(int AI_FileID, int AI_SigNo=SIGRTMIN, int AI_Flag=O_RDWR | /*O_NONBLOCK|*/O_ASYNC, pid_t AI_PId=::getpid())
|
|
{
|
|
if (::fcntl(AI_FileID, F_SETFL , AI_Flag ) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
if (::fcntl(AI_FileID, F_SETSIG, AI_SigNo) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
if (::fcntl(AI_FileID, F_SETOWN, AI_PId ) < 0)
|
|
{
|
|
return false;
|
|
}/*
|
|
if (::fcntl(AI_FileID, F_SETOWN, AI_PId ) < 0)*/
|
|
|
|
return true;
|
|
}/*
|
|
static bool SetupRTS(int AI_FileID, int AI_SigNo=SIGRTMIN, int AI_Flag=O_RDWR | O_NONBLOCK| O_ASYNC, int pid_t=::getpid())*/
|
|
|
|
#endif //__linux__
|
|
|
|
|
|
public:
|
|
};/*
|
|
class ZCSigAction*/
|
|
|
|
|
|
class ZCSigSet
|
|
{
|
|
private:
|
|
::sigset_t mi_SigSet;
|
|
public :
|
|
|
|
::sigset_t& GetSigSet()
|
|
{
|
|
return mi_SigSet;
|
|
}/*
|
|
::sigset_t& GetSigSet()*/
|
|
|
|
bool Empty()
|
|
{
|
|
return ::sigemptyset(&mi_SigSet)==0;
|
|
}/*
|
|
bool Empty()*/
|
|
|
|
bool Fill()
|
|
{
|
|
return ::sigfillset(&mi_SigSet)==0;
|
|
}/*
|
|
bool Fill()*/
|
|
|
|
bool Add(int AI_SigNo)
|
|
{
|
|
return ::sigaddset(&mi_SigSet, AI_SigNo)==0;
|
|
}/*
|
|
bool Add(int AI_SigNo)*/
|
|
|
|
bool Cut(int AI_SigNo)
|
|
{
|
|
return ::sigdelset(&mi_SigSet, AI_SigNo)==0;
|
|
}/*
|
|
bool Cut(int AI_SigNo)*/
|
|
|
|
bool IsMember(int AI_SigNo)
|
|
{
|
|
return ::sigismember(&mi_SigSet, AI_SigNo)==1;
|
|
}/*
|
|
bool IsMember(int AI_SigNo)*/
|
|
|
|
bool ProcMask(int AI_How, sigset_t* AP_OldSigSet=0)
|
|
{
|
|
return ::sigprocmask(AI_How, &mi_SigSet, AP_OldSigSet)==0;
|
|
}/*
|
|
bool ProcMask(int AI_How, sigset_t* AP_OldSigSet=0)*/
|
|
|
|
/*//////////////////////////////////////////////////////////////
|
|
|
|
■ int AI_How 가 가질 수 있는 값.
|
|
|
|
SIG_BLOCK
|
|
The set of blocked signals is the union of the current
|
|
set and the set argument.
|
|
|
|
지정된 시그널 마스크를 현재 시그널 블록 마스크에 추가한다.
|
|
블록된 시그널은 해당 시그널이 발생했을때, 지정된 시그널 핸
|
|
들러가 실행을 완료할 때까지 블럭된다.
|
|
|
|
블록되지 않은 시그널이라면, 해당 시그널이 발생했을때, 지정
|
|
된 시그널 핸들러를 수행하다가, 다른 시그널이 발생하면, 실행
|
|
을 중지하고, 그 시그널의 핸들러의 실행이 끝난 후에, 중지된
|
|
지점부터 재시작하게 된다.
|
|
|
|
SIG_UNBLOCK
|
|
The signals in set are removed from the current set of
|
|
blocked signals. It is legal to attempt to unblock a
|
|
signal which is not blocked.
|
|
|
|
지정된 시그널 마스크를 현재 시그널 블록 마스크에서 제거한다.
|
|
|
|
SIG_SETMASK
|
|
The set of blocked signals is set to the argument set.
|
|
|
|
-- 2011-06-09 21:37:00
|
|
|
|
//////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
bool Wait(int& ARRI_SigNo)
|
|
{
|
|
return ::sigwait(&mi_SigSet, &ARRI_SigNo)==0;
|
|
}/*
|
|
bool Wait(int& ARRI_SigNo)*/
|
|
|
|
int WaitInfo(siginfo_t& ARR_StSigInfo)
|
|
{
|
|
return ::sigwaitinfo(&mi_SigSet, &ARR_StSigInfo);
|
|
}/*
|
|
int WaitInfo(siginfo_t& ARR_StSigInfo)*/
|
|
|
|
/*//////////////////////////////////////////////////////////////////
|
|
|
|
■ SYNOPSIS
|
|
|
|
#include <signal.h>
|
|
|
|
int sigwaitinfo(const sigset_t *restrict set,
|
|
siginfo_t *restrict info);
|
|
|
|
int sigtimedwait( const sigset_t *restrict set ,
|
|
siginfo_t *restrict info,
|
|
const struct timespec *restrict timeout);
|
|
|
|
■ RETURN VALUES
|
|
|
|
Upon successful completion (that is, one of the signals
|
|
specified by set is pending or is generated) sigwaitinfo()
|
|
and sigtimedwait() will return the selected signal number.
|
|
Otherwise, the function returns -1 and sets errno to indi-
|
|
cate the error.
|
|
|
|
■ ERRORS
|
|
|
|
The sigwaitinfo() and sigtimedwait() functions will fail if:
|
|
|
|
EINTR The wait was interrupted by an unblocked, caught
|
|
signal.
|
|
|
|
ENOSYS The sigwaitinfo() and sigtimedwait() functions are
|
|
not supported.
|
|
|
|
The sigtimedwait() function will fail if:
|
|
|
|
EAGAIN No signal specified by set was generated within
|
|
the specified timeout period.
|
|
|
|
The sigwaitinfo() and sigtimedwait() functions may fail if:
|
|
|
|
EFAULT The set, info, or timeout argument points to an
|
|
invalid address.
|
|
|
|
The sigtimedwait() function may fail if:
|
|
|
|
EINVAL The timeout argument specified a tv_nsec value
|
|
less than zero or greater than or equal to 1000
|
|
million. The system only checks for this error if
|
|
no signal is pending in set and it is necessary to
|
|
wait.
|
|
|
|
■ -- 2011-06-02 01:14:00
|
|
|
|
//////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
static bool AddInfo(int AI_SignalNo, const union sigval AI_SigVal, int AI_ProcessID=::getpid())
|
|
{
|
|
return ::sigqueue(AI_ProcessID, AI_SignalNo, AI_SigVal)==0;
|
|
}/*
|
|
static bool AddInfo(int AI_SignalNo, const union sigval AI_SigVal, int AI_ProcessID=::getpid())*/
|
|
|
|
/*//////////////////////////////////////////////////////////////////
|
|
|
|
■ SYNOPSIS
|
|
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
|
|
union sigval{int sival_int; void* sival_ptr;};
|
|
|
|
int sigqueue(pid_t pid, int signo, const union sigval value);
|
|
|
|
■ RETURN VALUES
|
|
|
|
Upon successful completion, the specified signal will have
|
|
been queued, and the sigqueue() function returns 0. Other-
|
|
wise, the function returns -1 and sets errno to indicate the
|
|
error.
|
|
|
|
■ ERRORS
|
|
|
|
The sigqueue() function will fail if:
|
|
|
|
EAGAIN No resources are available to queue the signal.
|
|
The process has already queued SIGQUEUE_MAX sig-
|
|
nals that are still pending at the receiver(s),
|
|
or a system wide resource limit has been
|
|
exceeded.
|
|
|
|
EINVAL The value of signo is an invalid or unsupported
|
|
signal number.
|
|
|
|
ENOSYS The sigqueue() function is not supported by the
|
|
system.
|
|
|
|
EPERM The process does not have the appropriate
|
|
privilege to send the signal to the receiving
|
|
process.
|
|
|
|
ESRCH The process pid does not exist.
|
|
|
|
■ -- 2011-06-02 01:40:00
|
|
|
|
//////////////////////////////////////////////////////////////////*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCSigSet*/
|
|
|
|
|
|
|
|
class ZCProcess
|
|
{
|
|
public:
|
|
|
|
// cf) typedef int pid_t
|
|
|
|
// 데몬을 만드는 용도로도 가능한 함수이다.
|
|
|
|
bool Exec(const char* AP_ExeName, bool AB_DoCloseStdInOut=true)
|
|
{
|
|
return this->Exec(AP_ExeName, NULL, AB_DoCloseStdInOut);
|
|
}/*
|
|
bool Exec(const char* AP_ExeName, bool AB_DoCloseStdInOut=true)*/
|
|
|
|
bool Exec(const char* AP_ExeName, char* const APA_Arg[], bool AB_DoCloseStdInOut=true)
|
|
{
|
|
/*/////////////////////////////////////////////////////////////////
|
|
|
|
■ APA_Arg 의 마지막 원소는 NULL 로 끝나야 한다.
|
|
|
|
char *aRgu[4];
|
|
|
|
aRgu[0] = "/test/test.exe";
|
|
aRgu[1] = "ABC"; // Argu 1
|
|
aRgu[2] = "10" ; // Argu 2
|
|
aRgu[3] = 0 ; // 아규먼트가 끝이라는 것를 꼭 지정하여야 한다.
|
|
|
|
execvp(aRgu[0] , aRgu);
|
|
|
|
■ execl 사용례
|
|
|
|
::execl( "./WinSEC_D.exe" ,
|
|
"./WinSEC_D.exe" ,
|
|
"WinSEC_Conf.txt",
|
|
NULL
|
|
////// );
|
|
|
|
/////////////////////////////////////////////////////////////////*/
|
|
|
|
if(AP_ExeName==0 || AP_ExeName[0]==0) return false;
|
|
|
|
int VI_ForkNum = ::fork(); /*####################*/
|
|
|
|
if(VI_ForkNum==0) // child
|
|
{
|
|
if(AB_DoCloseStdInOut==true)
|
|
{
|
|
::close(0);
|
|
::close(1);
|
|
}/*
|
|
if(AB_DoCloseStdInOut==true)*/
|
|
|
|
::setsid(); // 자기 자신을 세션의 리더로 만든다.
|
|
::execvp(AP_ExeName, APA_Arg);
|
|
|
|
return true;
|
|
}
|
|
else if(VI_ForkNum==-1) // error
|
|
{
|
|
return false;
|
|
}
|
|
else // 부모의 경우
|
|
{
|
|
return true;
|
|
}/*
|
|
else*/
|
|
}/*
|
|
bool Exec(const char* AP_ExeName, char* const APA_Arg[], bool AB_DoCloseStdInOut=true)*/
|
|
|
|
static pid_t Fork (){return ::fork ();}
|
|
static int GetPPID(){return ::getppid();}
|
|
static int GetUID (){return ::getuid ();}
|
|
static int GetGID (){return ::getgid ();}
|
|
static long GetPID (){return ::getpid ();}
|
|
|
|
public:
|
|
};/*
|
|
class ZCProcess*/
|
|
|
|
|
|
class ZCMemMap
|
|
{
|
|
public:
|
|
|
|
static void* LinkMap(
|
|
int AI_FileDesc, off_t AL_Offset=0, size_t AL_MapSize=0, int AI_Protect=PROT_READ | PROT_WRITE, int AI_Flags=MAP_SHARED, void* AP_BaseAddress=NULL)
|
|
{
|
|
return ::mmap(AP_BaseAddress, AL_MapSize, AI_Protect, AI_Flags, AI_FileDesc, AL_Offset);
|
|
}/*
|
|
static void* LinkMap(
|
|
int AI_FileDesc, off_t AL_Offset=0, size_t AL_MapSize=0, int AI_Protect=PROT_READ | PROT_WRITE, int AI_Flags=MAP_SHARED, void* AP_BaseAddress=NULL) */
|
|
|
|
/* munmap(void*,size_t)
|
|
|
|
Upon successful completion, munmap() returns 0. Otherwise,
|
|
it returns -1 and sets errno to indicate the error */
|
|
|
|
static bool UnMap(void* AP_BaseAddress, size_t AL_Size)
|
|
{
|
|
return ::munmap(AP_BaseAddress, AL_Size)==0;
|
|
}/*
|
|
static bool UnMap(void* AP_BaseAddress, size_t AL_Size)*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCMemMap*/
|
|
|
|
|
|
/*/////////////////////////////////////////////////////////////////////////////
|
|
|
|
SHMGET(2) 리눅스 프로그래머 메뉴얼 SHMGET(2)
|
|
|
|
■ 이름
|
|
|
|
shmget - 공유 메모리 세그먼트를 할당한다.
|
|
|
|
■ 사용법
|
|
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
|
|
int shmget(key_t key, int size, int shmflg);
|
|
|
|
■ 설명
|
|
shmget() 는 key 인자값과 관련된 공유 메모리 세그먼트 식별자를 반환한다.
|
|
만일, key 가 IPC_PRIVATE 값을 가지고 있거나 또는 key 가 IPC_PRIVATE 가
|
|
아 니고, key 와 연계되어 있는 공유메모리 세그먼트가 없다면 PAGE_SIZE 의
|
|
배수만큼의 size 를 가지고 있는 새로운 공유 메모리 세그먼트가 만들 어 진
|
|
다. IPC_CREAT 는 shmflg 에 명시되어 있다. (i.e. shmflg&IPC_CREAT 는
|
|
0이 아니다.)
|
|
|
|
shmflg 의 구성은 다음과 같다:
|
|
|
|
IPC_CREAT 새로운 세그먼트를 만든다. 만일 이 플래그가 사용되지 않는 다
|
|
면, shmget() 는 key와 관련된 세그먼트를 찾을 것이며, 사용자
|
|
가 그 세그먼트와 관련된 shmid 를 받을 허가권이 있는지 알 기
|
|
위해서 검사한다. 그리고 세그먼트가 파괴되었다는 표시를 하지
|
|
않도록 보장한다.
|
|
|
|
IPC_EXCL 세그먼트가 존재할경우 실패를 보장하기 위해 IPC_CREAT와 함께
|
|
사용된다.
|
|
|
|
mode_flags (lowest 9 bits)
|
|
소 유 자, 그룹, 그 외들을 보장하기 위해 허가권을 지정한다.
|
|
현재, 실행 허가권은 시스템에 의해 사용되지 않는다.
|
|
|
|
새로운 세그먼트가 생성된다면, shmflg 의 접근 허가권은세그먼트에 정의 되
|
|
어 있는 shmid_ds 의 shm_perm 멤버로 복사된다. shmid_ds 구조체:
|
|
|
|
struct shmid_ds {
|
|
struct ipc_perm shm_perm; // 퍼미션
|
|
int shm_segsz; // 세그먼트의 크기(bytes)
|
|
time_t shm_atime; // 마지막 접근 시간
|
|
time_t shm_dtime; // 마지막 제거 시간
|
|
time_t shm_ctime; // 마지막 변경 시간
|
|
unsigned short shm_cpid; // 생성자의 pid
|
|
unsigned short shm_lpid; // 마지막으로 작동한 프로세스 pid
|
|
short shm_nattch; // 현재 접근한 프로세스의 수
|
|
};
|
|
|
|
|
|
struct ipc_perm
|
|
{
|
|
key_t key;
|
|
ushort uid; // 소유자의 euid 와 egid
|
|
ushort gid;
|
|
ushort cuid; // 생성자의 euid 와 egid
|
|
ushort cgid;
|
|
ushort mode; // shmflg의 하위 9비트
|
|
ushort seq; // 연속 수(sequence number)
|
|
};
|
|
|
|
게다가, 생성되는 동안 시스템 콜은 시스템 공유 메모리 세그먼트 데이터 구
|
|
조 shmid_ds 를 다음과 같이 초기화한다.
|
|
|
|
shm_perm.cuid 와 shm_perm.uid 는 호출 프로세스의 유효 user-ID 로
|
|
설정된다.
|
|
|
|
shm_perm.cgid 와 shm_perm.gid 는 호출 프로세스의 유효 group-ID로
|
|
설정된다.
|
|
|
|
shm_perm.mode 의 하위 9비트들은 shmflg 의 하위 9비트들로 설정 된
|
|
다.
|
|
|
|
shm_segsz 는 size 값으로 설정된다.
|
|
|
|
shm_lpid, shm_nattch, shm_atime 그리고 shm_dtime 는 0 으로 설정
|
|
된다.
|
|
|
|
shm_ctime 는 현재 시간으로 설정된다.
|
|
|
|
만일 공유 메모리 세그먼트가 이미 존재한다면, 접근 허가권이 조사되며, 파
|
|
괴도도록 표시되어 있는지 알아보기 위해 검사한다.
|
|
|
|
■ SYSTEM CALLS
|
|
|
|
fork() fork() 후에 자식 프로세스는 연결된 공유 메모리 세그먼트들을 상속
|
|
한다.
|
|
|
|
exec() exec() 후에 연결된 모든 공유 메모리 세그먼트는 분리된다.(파괴 되
|
|
는것이 아니다)
|
|
|
|
exit() exit() 시 연결된 모든 공유 메모리 세그먼트는 분리된다.(파괴되는
|
|
것이 아니다)
|
|
|
|
■ 반환값
|
|
|
|
성공시 유효한 세그먼트 식별자 shmid 가 반환되며, 에러시 -1이 반환된 다.
|
|
|
|
■ 에러
|
|
|
|
실패시, errno 는 다음중 하나로 설정된다:
|
|
|
|
EINVAL 만일 SHMMIN > size, 또는 size > SHMMAX, 또는 size이 세그먼
|
|
트의 크기보다 크다면 이에러가 반환된다.
|
|
|
|
EEXIST IPC_CREAT | IPC_EXCL 이 지정되어 있고,
|
|
세그먼트가 존재하 면 이 에러가 반환된다.
|
|
|
|
EIDRM 세그먼트가 파괴나 제거되도록 표시되어 있다면 이 에러가 반환된다.
|
|
|
|
ENOSPC 가능한 모든 공유 메모리 id (SHMMNI) 를 가졌거나 요청된 size 의
|
|
세그먼트 할당이 시스템 전체 공유 메모리 제한값 (SHMALL) 을 초과할경우 반환된다.
|
|
|
|
ENOENT 주어진 key에 해당하는 세그먼트가 존재하지 않고,
|
|
IPC_CREAT 가 지정되지 않았다면 반환된다.
|
|
|
|
EACCES 사용자가 공유 메모리 세그먼트에 대한 접근 허가권이 없을때 반환된다.
|
|
|
|
ENOMEM 세그먼트를 위해 할당할 메모리가 없을때 반환된다.
|
|
|
|
※ 주의
|
|
|
|
IPC_PRIVATE 는 플레그 필드가 아니라 key_t 타입이다. 이 특별한 값이 key에
|
|
사용된다면, 시스템 콜은 shmflg 의 하위 9비트들외 모든것을 무시한다.
|
|
그리고 새 공유 메모리 세그먼트를 생성(성공시)한다.
|
|
|
|
다음은 shmget 시스템 콜에 영향을 주는 공유 메모리 세그먼트 자원들의 제한값들이다:
|
|
|
|
SHMALL 시스템 전체의 공유 메모리 세그먼트 최대 값: 정책 의존적이다.
|
|
SHMMAX 공유 메모리 세그먼트의 최대 크기(바이트수): 수행 의존적이다.(현재 4M)
|
|
SHMMIN 공유 메모리 세그먼트의 최소 크기(바이트수): 수행 의존적이다.
|
|
(PAGE_SIZE 가 유효한 최소 크기이지만, 현재는 1byte이다.)
|
|
SHMMNI 시스템 전체의 공유 메모리 세그먼트 최대 수: 수행 의존적이다(현재 4096)
|
|
|
|
수행시 프로세스당 공유 메모리 세그먼트의 특별한 제한은 없다. (SHMSEG)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
// 공유메모리 클래스
|
|
|
|
class ZCShareMemory
|
|
{
|
|
private:
|
|
enum{EInvalidID=-1};
|
|
private:
|
|
ZTypeID mh_FileMap;
|
|
void* mp_Address;
|
|
public :
|
|
|
|
ZCShareMemory()
|
|
{
|
|
mh_FileMap= EInvalidID;
|
|
mp_Address= 0 ;
|
|
}/*
|
|
ZCShareMemory()*/
|
|
|
|
ZTypeID GetID() const
|
|
{
|
|
return mh_FileMap;
|
|
}/*
|
|
ZTypeID GetID() const*/
|
|
|
|
bool IsValidID() const
|
|
{
|
|
return mh_FileMap != -1;
|
|
}/*
|
|
bool IsValidID() const*/
|
|
|
|
void* GetStartAddress() const
|
|
{
|
|
return mp_Address;
|
|
}/*
|
|
void* GetStartAddress() const*/
|
|
|
|
bool Create(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return Create((key_t)(ZNsMain::ZfGetKeyNum(AP_MapName)), AL_MapSize, AI_Flag);
|
|
}/*
|
|
bool Create(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool Create(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
if((mh_FileMap=::shmget((key_t)AL_MapID, AL_MapSize, AI_Flag))==-1)
|
|
{
|
|
if(errno==EEXIST)
|
|
{
|
|
return (mh_FileMap=::shmget(AL_MapID, 0, 0)) != -1;
|
|
}/*
|
|
if(errno==EEXIST)*/
|
|
|
|
return false;
|
|
}/*
|
|
if((mh_FileMap=::shmget((key_t)AL_MapID, AL_MapSize, AI_Flag))==-1)*/
|
|
|
|
return true;
|
|
}/*
|
|
bool Create(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
|
|
bool ShmGet(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return (mh_FileMap=::shmget((key_t)(ZNsMain::ZfGetKeyNum(AP_MapName)), AL_MapSize, AI_Flag))!=-1 ;
|
|
}/*
|
|
bool ShmGet(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool ShmGet(const char* AP_MapName)
|
|
{
|
|
return (mh_FileMap=::shmget((key_t)(ZNsMain::ZfGetKeyNum(AP_MapName)), 0 ,0))!=-1 ;
|
|
}/*
|
|
bool ShmGet(const char* AP_MapName)*/
|
|
|
|
bool ShmGet(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return (mh_FileMap=::shmget((key_t)AL_MapID, AL_MapSize, AI_Flag))!=-1 ;
|
|
}/*
|
|
bool ShmGet(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool ShmGet(long AL_MapID)
|
|
{
|
|
// key 가 AL_MapID 인 공유메모리가 있는지 조사
|
|
|
|
return (mh_FileMap=::shmget((key_t)AL_MapID, 0, 0))!=-1 ;
|
|
}/*
|
|
bool ShmGet(long AL_MapID)*/
|
|
|
|
|
|
bool LinkMap()
|
|
{
|
|
// void *shmat(int shmid, const void *shmaddr, int shmflg);
|
|
|
|
return (mp_Address=::shmat(mh_FileMap, (void*)0, 0))!=NULL ;
|
|
|
|
// shmat() 의 3 번째 인수로 SHM_RDONLY 플래그를 지정하면
|
|
// 공유 메모리 세그먼트는 매핑되어지지만, 읽기전용(readonly)로 표시된다
|
|
}/*
|
|
bool LinkMap()*/
|
|
|
|
bool UnMap()
|
|
{
|
|
// cf) In Window : BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
|
|
|
|
bool VB_IsOK=(::shmdt(mp_Address)!=-1); mp_Address=0; return VB_IsOK;
|
|
}/*
|
|
bool UnMap()*/
|
|
|
|
bool Close()
|
|
{
|
|
bool VB_IsOK =
|
|
( ::shmctl(mh_FileMap, IPC_RMID, 0) != -1 ) ;
|
|
|
|
mh_FileMap=EInvalidID; return VB_IsOK;
|
|
|
|
/* IPC_RMID 명령어는 실제로 커널로 부터 세그먼트를 제거하는 것은 아니다.
|
|
제거하기 위해 세그먼트에 표시를 해둔다. 실제적인 제거 자체는 현재 세그먼트에
|
|
붙어 있는(attached) 마지막 프로세스가 적절히 분리됐을(detached) 때 일어난다.
|
|
물론, 현재 세그먼트에 붙어있는 (attached) 프로세스가 없으면, 제거는 즉시
|
|
이루어 진다. */
|
|
}/*
|
|
bool Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCShareMemory*/
|
|
|
|
|
|
/*////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ class ZCProcessMutex 는 프로세스의 뮤텍스.
|
|
|
|
Window 에서는 이름있는 mutex 를 쓰면 간단하지만, Linux 에서는 세마포어를 쓸 수도 있다.
|
|
pthread 라이브러리를 써서 공유메모리에 mutex 데이타를 넣어도 되지만 그만큼 잔손질이
|
|
필요하다.
|
|
|
|
Linux 에서는 다중쓰레드에서 쓰는 세마포어와 다중 프로세스에서 쓰는 세마포어가 다르다.
|
|
정확히는 pthread 가 프로세스 세마포어를 아직 지원하지 않는다. (2006-12-28 16:02:00)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
class ZCProcessMutex
|
|
{
|
|
private:
|
|
ZTypeID mh_Mutex;
|
|
private:
|
|
|
|
bool Init(int AI_InitNum=1)
|
|
{
|
|
// 리눅스에서만 가능
|
|
// 세마포어의 값을 초기화한다.
|
|
|
|
union semun SemUnion; SemUnion.val=AI_InitNum;
|
|
|
|
return ::semctl(mh_Mutex, 0, SETVAL, SemUnion)!=-1 ;
|
|
|
|
/*//////////////////////////////////////////////////////////
|
|
|
|
int semctl(int semid, int semnum, int cmd, ...)
|
|
|
|
If successful, the value returned by semctl() depends on cmd as follows:
|
|
GETVAL
|
|
The value of semval.
|
|
GETPID
|
|
The value of sempid.
|
|
GETNCNT
|
|
The value of semncnt.
|
|
GETZCNT
|
|
The value of semzcnt.
|
|
All others
|
|
0.
|
|
Otherwise, semctl() returns -1 and errno indicates the error
|
|
|
|
//////////////////////////////////////////////////////////*/
|
|
}/*
|
|
bool Init(int AI_InitNum=1)*/
|
|
|
|
/*private:*/
|
|
public :
|
|
|
|
ZCProcessMutex()
|
|
{
|
|
mh_Mutex=0;
|
|
}/*
|
|
ZCProcessMutex()*/
|
|
|
|
ZTypeID GetHandle() const
|
|
{
|
|
return mh_Mutex;
|
|
}/*
|
|
ZTypeID GetHandle() const*/
|
|
|
|
|
|
// Linux 의 경우 프로세스 세마포어로 Window 의 프로세스 뮤텍스를 구현한다.
|
|
// Window 의 뮤텍스는 쓰레드와 프로세스에 모두 사용할 수 있지만 Linux 의
|
|
// 경우에는 그렇지 않다. 2006-08-06 15:55:00
|
|
|
|
bool Make(const char* AP_MutexName, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return this->Make(ZNsMain::ZfGetKeyNum(AP_MutexName), AI_Flag);
|
|
}/*
|
|
bool Make(const char* AP_MutexName, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool Make(long AL_KeyID, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
// AI_Flag 는 IPC_CREAT,IPC_EXCL,0 의 세 값중의 하나이며
|
|
// 0 이면 해당 동기화object 가 존재할 때만 연다.
|
|
|
|
// int semget(key_t key,int nsems,int semflg)
|
|
|
|
if((mh_Mutex=::semget((key_t)AL_KeyID, 1, AI_Flag))==-1)
|
|
{
|
|
if(errno==EEXIST)
|
|
{
|
|
return (mh_Mutex=::semget((key_t)AL_KeyID, 1, 0)) != -1;
|
|
}/*
|
|
if(errno==EEXIST)*/
|
|
|
|
return false;
|
|
}/*
|
|
if((mh_Mutex=::semget((key_t)AL_KeyID, 1, AI_Flag))==-1)*/
|
|
|
|
this->Init(); return true;
|
|
}/*
|
|
bool Make(long AL_KeyID, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool Lock()
|
|
{
|
|
struct sembuf sembuf_Obj ;
|
|
sembuf_Obj.sem_num = 0 ; // semaphore 배열에서 몇 번째 semaphore 인지를 지정
|
|
sembuf_Obj.sem_op = -1 ;
|
|
sembuf_Obj.sem_flg = SEM_UNDO ;
|
|
|
|
return ::semop(mh_Mutex, &sembuf_Obj, 1)==0 ;
|
|
}/*
|
|
bool Lock()*/
|
|
|
|
bool UnLock()
|
|
{
|
|
struct sembuf sembuf_Obj ;
|
|
sembuf_Obj.sem_num = 0 ; // semaphore 배열에서 몇 번째 semaphore 인지를 지정
|
|
sembuf_Obj.sem_op = 1 ;
|
|
sembuf_Obj.sem_flg = SEM_UNDO ;
|
|
|
|
return ::semop(mh_Mutex, &sembuf_Obj, 1)==0 ;
|
|
}/*
|
|
bool UnLock()*/
|
|
|
|
bool Close()
|
|
{
|
|
// 커널에서 semaphore 가 제거된다. 이 함수 수행후에 ipcs 해 보면
|
|
// semaphore 목록에 나타나지 않는다. 따라서 이 함 수행 후에는 Lock()
|
|
// 에서 대기하고 있던 프로세스나 쓰레드는 한꺼번에 깨어나고 더이상
|
|
// 동기화되지 않는다. 프로세스가 이 함수를 수행하지 않고 종료해도 커
|
|
// 널은 자동적으로 semaphore 를 삭제하지 않는다.
|
|
|
|
union semun SemUnion ;
|
|
|
|
const bool CB_IsOK =
|
|
(::semctl(mh_Mutex, 0, IPC_RMID, SemUnion) != -1);
|
|
|
|
mh_Mutex=0; return CB_IsOK;
|
|
}/*
|
|
bool Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCProcessMutex*/
|
|
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
/////////// end class ZCProcessMutex ///////////
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
|
|
|
|
// posix mutex 로 프로세스간 동기화를 하는 클래스
|
|
|
|
|
|
class ZCProcessMutexPosix
|
|
{
|
|
private:
|
|
|
|
class ZCMutexData
|
|
{
|
|
protected:
|
|
friend class ZCProcessMutexPosix;
|
|
protected:
|
|
pthread_mutex_t mo_PThreadMutex ;
|
|
pthread_mutexattr_t mo_PThreadMutexAttr;
|
|
protected:
|
|
};/*
|
|
class ZCMutexData*/
|
|
|
|
private:
|
|
ZCMutexData* mp_CMutexData ;
|
|
ZCShareMemory mo_CShareMemory;
|
|
public :
|
|
|
|
ZCProcessMutexPosix()
|
|
{
|
|
mp_CMutexData=0;
|
|
}/*
|
|
ZCProcessMutexPosix()*/
|
|
|
|
bool Make(LPCTSTR AP_MapName)
|
|
{
|
|
return Make( ZNsMain::ZfGetKeyNum(AP_MapName) );
|
|
}/*
|
|
bool Make(LPCTSTR AP_MapName)*/
|
|
|
|
bool Make(long AL_MapKey)
|
|
{
|
|
if(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCMutexData))==false)
|
|
{
|
|
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MapKey)!=true)
|
|
{
|
|
return false;
|
|
}
|
|
if(mo_CShareMemory.LinkMap()==false)
|
|
{
|
|
mo_CShareMemory.Close(); return false;
|
|
}/*
|
|
if(mo_CShareMemory.LinkMap()==false)*/
|
|
|
|
mp_CMutexData=(ZCMutexData*)mo_CShareMemory.GetStartAddress();
|
|
}
|
|
else // mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCMutexData))==true
|
|
{
|
|
if(mo_CShareMemory.LinkMap()==false)
|
|
{
|
|
mo_CShareMemory.Close(); return false;
|
|
}/*
|
|
if(mo_CShareMemory.LinkMap()==false)*/
|
|
|
|
mp_CMutexData = (ZCMutexData*)mo_CShareMemory.GetStartAddress();
|
|
|
|
::memset(mp_CMutexData, 0x00, sizeof(ZCMutexData));
|
|
|
|
::pthread_mutexattr_init (&mp_CMutexData->mo_PThreadMutexAttr);
|
|
::pthread_mutexattr_settype (&mp_CMutexData->mo_PThreadMutexAttr, PTHREAD_MUTEX_RECURSIVE); // 이전에는 PTHREAD_MUTEX_ERRORCHECK 로 설정했었다. 2007-05-24 20:04:00
|
|
::pthread_mutexattr_setpshared(&mp_CMutexData->mo_PThreadMutexAttr, PTHREAD_PROCESS_SHARED );
|
|
::pthread_mutex_init (&mp_CMutexData->mo_PThreadMutex, (const pthread_mutexattr_t*)&mp_CMutexData->mo_PThreadMutexAttr);
|
|
}/*
|
|
else // mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCMutexData))==true*/
|
|
|
|
return true;
|
|
}/*
|
|
bool Make(long AL_MapKey)*/
|
|
|
|
|
|
bool Lock()
|
|
{
|
|
return this->LockRaw()==0;
|
|
}/*
|
|
int Lock()*/
|
|
|
|
int LockRaw()
|
|
{
|
|
return ::pthread_mutex_lock(&mp_CMutexData->mo_PThreadMutex);
|
|
|
|
// EDEADLK 를 반환하는 경우를 체크할 것.
|
|
}/*
|
|
int Lock()*/
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
bool Lock(const struct timespec& AR_TimeSpec)
|
|
{
|
|
return this->LockRaw(AR_TimeSpec)==0;
|
|
}/*
|
|
int Lock(const struct timespec& AR_TimeSpec)*/
|
|
|
|
bool Lock(long AL_MiliSec)
|
|
{
|
|
return this->LockRaw(AL_MiliSec)==0;
|
|
}/*
|
|
int Lock(long AL_MiliSec)*/
|
|
|
|
int LockRaw(const struct timespec& AR_TimeSpec)
|
|
{
|
|
return ::pthread_mutex_timedlock(&mp_CMutexData->mo_PThreadMutex, &AR_TimeSpec);
|
|
|
|
// 성공이면 ZNsEnum::ZEThread_OK 를
|
|
// 시간초과이면 ZNsEnum::ZEThread_TimeOut 를 리턴
|
|
//
|
|
// EDEADLK 를 반환하는 경우를 체크할 것.
|
|
}/*
|
|
int Lock(const struct timespec& AR_TimeSpec)*/
|
|
|
|
int LockRaw(long AL_MiliSec)
|
|
{
|
|
timespec VO_TimeSpec;
|
|
|
|
VO_TimeSpec.tv_sec = time(NULL) + AL_MiliSec/1000 ;
|
|
VO_TimeSpec.tv_nsec = (AL_MiliSec%1000)* 1000*1000 ;
|
|
|
|
return ::pthread_mutex_timedlock
|
|
(&mp_CMutexData->mo_PThreadMutex, &VO_TimeSpec);
|
|
}/*
|
|
int Lock(long AL_MiliSec)*/
|
|
|
|
int LockTime(long AL_MiliSec)
|
|
{
|
|
return LockRaw(AL_MiliSec);
|
|
}/*
|
|
int LockTime(long AL_MiliSec)*/
|
|
|
|
#endif // __USE_XOPEN2K
|
|
|
|
bool UnLock()
|
|
{
|
|
return ::pthread_mutex_unlock(&mp_CMutexData->mo_PThreadMutex)==0;
|
|
}/*
|
|
bool UnLock()*/
|
|
|
|
void Close()
|
|
{
|
|
::pthread_mutex_destroy(&mp_CMutexData->mo_PThreadMutex);
|
|
|
|
mo_CShareMemory.UnMap();
|
|
mo_CShareMemory.Close();
|
|
}/*
|
|
void Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCProcessMutexPosix*/
|
|
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
|
|
/*////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 기록자/판독자 lock(read-write lock) 으로 Window 의 수동리셋 이벤트를 구현할 수
|
|
있다. 즉 특정 지점에서 다수의 쓰레드가 대기하다가 동시에 깨어나는 동작이 가능하
|
|
다. 물론 뮤텍스 조건 변수로 수동리셋 이벤트를 구현하는 것이 더 효과적이다.
|
|
|
|
■ POSIX.1b structure for a time value.
|
|
This is like a `struct timeval' but has nanoseconds instead of microseconds.
|
|
|
|
struct timespec
|
|
{
|
|
__time_t tv_sec ; // Seconds.
|
|
long int tv_nsec; // Nanoseconds. 10 억분의 1 초, 1 밀리초=1000*1000 나노초
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
class ZCLockRW_Base
|
|
{
|
|
protected:
|
|
pthread_rwlock_t mo_pthread_rwlock_t;
|
|
public:
|
|
|
|
int Init()
|
|
{
|
|
return ::pthread_rwlock_init(&mo_pthread_rwlock_t, NULL);
|
|
}/*
|
|
int Init()*/
|
|
|
|
int LockRead()
|
|
{
|
|
return ::pthread_rwlock_rdlock(&mo_pthread_rwlock_t);
|
|
}/*
|
|
int LockRead()*/
|
|
|
|
int LockRead(const struct timespec& AR_TimeSpec)
|
|
{
|
|
return ::pthread_rwlock_timedrdlock(&mo_pthread_rwlock_t, &AR_TimeSpec);
|
|
}/*
|
|
int LockRead(const struct timespec& AR_TimeSpec)*/
|
|
|
|
int LockRead(long AL_MiliSec)
|
|
{
|
|
timespec VO_TimeSpec;
|
|
|
|
VO_TimeSpec.tv_sec =time(NULL)+AL_MiliSec/1000;
|
|
VO_TimeSpec.tv_nsec=(AL_MiliSec%1000)*1000*1000;
|
|
|
|
return ::pthread_rwlock_timedrdlock(&mo_pthread_rwlock_t, &VO_TimeSpec);
|
|
}/*
|
|
int LockRead(long AL_MiliSec)*/
|
|
|
|
int TryLockRead()
|
|
{
|
|
return ::pthread_rwlock_tryrdlock(&mo_pthread_rwlock_t);
|
|
}/*
|
|
int TryLockRead()*/
|
|
|
|
int LockWrite()
|
|
{
|
|
return ::pthread_rwlock_wrlock(&mo_pthread_rwlock_t);
|
|
}/*
|
|
int LockRead()*/
|
|
|
|
int LockWrite(const struct timespec& AR_TimeSpec)
|
|
{
|
|
return ::pthread_rwlock_timedwrlock(&mo_pthread_rwlock_t,&AR_TimeSpec);
|
|
}/*
|
|
int LockWrite(const struct timespec& AR_TimeSpec)*/
|
|
|
|
int LockWrite(long AL_MiliSec)
|
|
{
|
|
timespec VO_TimeSpec;
|
|
|
|
VO_TimeSpec.tv_sec =time(NULL)+AL_MiliSec/1000;
|
|
VO_TimeSpec.tv_nsec=(AL_MiliSec%1000)*1000*1000;
|
|
|
|
return ::pthread_rwlock_timedwrlock(&mo_pthread_rwlock_t, &VO_TimeSpec);
|
|
}/*
|
|
int LockWrite(long AL_MiliSec)*/
|
|
|
|
int TryLockWrite()
|
|
{
|
|
return ::pthread_rwlock_trywrlock(&mo_pthread_rwlock_t);
|
|
}/*
|
|
int TryLockWrite()*/
|
|
|
|
int UnLock()
|
|
{
|
|
return ::pthread_rwlock_unlock(&mo_pthread_rwlock_t);
|
|
}/*
|
|
int UnLock()*/
|
|
|
|
int Close()
|
|
{
|
|
return ::pthread_rwlock_destroy(&mo_pthread_rwlock_t);
|
|
}/*
|
|
int Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCLockRW_Base*/
|
|
|
|
|
|
class ZCLockRW : public ZCLockRW_Base
|
|
{
|
|
protected:
|
|
pthread_rwlockattr_t mo_pthread_rwlockattr_t;
|
|
public :
|
|
|
|
int InitAttr()
|
|
{
|
|
return ::pthread_rwlockattr_init(&mo_pthread_rwlockattr_t);
|
|
}/*
|
|
int InitAttr()*/
|
|
|
|
int Init()
|
|
{
|
|
return ::pthread_rwlock_init(&mo_pthread_rwlock_t, &mo_pthread_rwlockattr_t);
|
|
}/*
|
|
int Init()*/
|
|
|
|
bool SetShared(int AI_PShared=PTHREAD_PROCESS_PRIVATE)
|
|
{
|
|
return ::pthread_rwlockattr_setpshared(&mo_pthread_rwlockattr_t, AI_PShared)==ZNsEnum::ZEThread_OK;
|
|
}/*
|
|
bool SetShared(int AI_PShared=PTHREAD_PROCESS_PRIVATE)*/
|
|
|
|
bool GetShared(int& ARRI_PShared)
|
|
{
|
|
return ::pthread_rwlockattr_getpshared(&mo_pthread_rwlockattr_t, &ARRI_PShared)==ZNsEnum::ZEThread_OK;
|
|
}/*
|
|
bool GetShared(int& ARRI_PShared)*/
|
|
|
|
|
|
// 판독자,기록자 lock 의 우선순위 관련 함수, 현재 Posix 표준이 아니다.
|
|
// 이 함수로 우선순위를 설정하지 않았다면 기본적으로 기록자 lock 에
|
|
// 우선순위를 두는 것으로 설정되있는 베포판이 많다. -- 2006-12-30 16:21:00
|
|
|
|
int GetKind(int& ARRI_Kind) const
|
|
{
|
|
return ::pthread_rwlockattr_getkind_np(&mo_pthread_rwlockattr_t, &ARRI_Kind);
|
|
}/*
|
|
int GetKind(int& ARRI_Kind) const*/
|
|
|
|
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)
|
|
{
|
|
return ::pthread_rwlockattr_setkind_np(&mo_pthread_rwlockattr_t, AI_Kind);
|
|
}/*
|
|
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)*/
|
|
|
|
|
|
int CloseAttr()
|
|
{
|
|
return ::pthread_rwlockattr_destroy(&mo_pthread_rwlockattr_t);
|
|
}/*
|
|
int CloseAttr()*/
|
|
|
|
int Close()
|
|
{
|
|
return (ZCLockRW_Base::Close(), CloseAttr());
|
|
}/*
|
|
int Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCLockRW*/
|
|
|
|
|
|
class ZCLockRW_Process
|
|
{
|
|
protected:
|
|
ZCLockRW* mp_CLockRW ;
|
|
ZCShareMemory mo_CShareMemory;
|
|
public :
|
|
|
|
ZCLockRW_Process()
|
|
{
|
|
mp_CLockRW=0;
|
|
}/*
|
|
ZCLockRW_Process()*/
|
|
|
|
bool Make(long AL_MapKey)
|
|
{
|
|
if(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCLockRW))==false)
|
|
{
|
|
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MapKey)!=true)
|
|
{
|
|
return false;
|
|
}
|
|
if(mo_CShareMemory.LinkMap()==false)
|
|
{
|
|
mo_CShareMemory.Close(); return false;
|
|
}/*
|
|
if(mo_CShareMemory.LinkMap()==false)*/
|
|
|
|
mp_CLockRW=(ZCLockRW*)mo_CShareMemory.GetStartAddress();
|
|
}
|
|
else //!(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCLockRW))==false)
|
|
{
|
|
if(mo_CShareMemory.LinkMap()==false)
|
|
{
|
|
mo_CShareMemory.Close(); return false;
|
|
}/*
|
|
if(mo_CShareMemory.LinkMap()==false)*/
|
|
|
|
mp_CLockRW=(ZCLockRW*)mo_CShareMemory.GetStartAddress();
|
|
|
|
::memset(mp_CLockRW,0x00,sizeof(ZCLockRW));
|
|
|
|
mp_CLockRW->InitAttr();
|
|
mp_CLockRW->SetShared(PTHREAD_PROCESS_SHARED);
|
|
mp_CLockRW->Init();
|
|
}/*
|
|
else //!(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCLockRW))==false)*/
|
|
|
|
return true;
|
|
}/*
|
|
bool Make(long AL_MapKey)*/
|
|
|
|
bool Make(const char* AP_KeyName)
|
|
{
|
|
int Index =-1 ;
|
|
long VL_KeyID = 10 ;
|
|
|
|
if(AP_KeyName!=0)
|
|
{
|
|
while(AP_KeyName[++Index]!=0)
|
|
{
|
|
VL_KeyID += AP_KeyName[Index]*(Index+1) ;
|
|
}/*
|
|
while(AP_KeyName[++Index]!=0)*/
|
|
}/*
|
|
if(AP_KeyName!=0)*/
|
|
|
|
return this->Make(VL_KeyID);
|
|
}/*
|
|
bool Make(const char* AP_KeyName)*/
|
|
|
|
int LockRead()
|
|
{
|
|
return mp_CLockRW->LockRead();
|
|
}/*
|
|
int LockRead()*/
|
|
|
|
int LockRead(const struct timespec& AR_TimeSpec)
|
|
{
|
|
return mp_CLockRW->LockRead(AR_TimeSpec);
|
|
}/*
|
|
int LockRead(const struct timespec& AR_TimeSpec)*/
|
|
|
|
int TryLockRead()
|
|
{
|
|
return mp_CLockRW->TryLockRead();
|
|
}/*
|
|
int TryLockRead()*/
|
|
|
|
int LockWrite()
|
|
{
|
|
return mp_CLockRW->LockWrite();
|
|
}/*
|
|
int LockRead()*/
|
|
|
|
int LockWrite(const struct timespec& AR_TimeSpec)
|
|
{
|
|
return mp_CLockRW->LockWrite(AR_TimeSpec);
|
|
}/*
|
|
int LockWrite(const struct timespec& AR_TimeSpec)*/
|
|
|
|
int TryLockWrite()
|
|
{
|
|
return mp_CLockRW->TryLockWrite();
|
|
}/*
|
|
int TryLockWrite()*/
|
|
|
|
int UnLock()
|
|
{
|
|
return mp_CLockRW->UnLock();
|
|
}/*
|
|
int UnLock()*/
|
|
|
|
int GetKind(int& ARRI_Kind) const
|
|
{
|
|
return mp_CLockRW->GetKind(RR(ARRI_Kind));
|
|
}/*
|
|
int GetKind(int& ARRI_Kind) const*/
|
|
|
|
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)
|
|
{
|
|
return mp_CLockRW->SetKind(AI_Kind);
|
|
}/*
|
|
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)*/
|
|
|
|
int Close()
|
|
{
|
|
int VI_Resutl=mp_CLockRW->Close();
|
|
|
|
mo_CShareMemory.UnMap();
|
|
mo_CShareMemory.Close();
|
|
|
|
return VI_Resutl;
|
|
}/*
|
|
int Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCLockRW_Process*/
|
|
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ class ZCProcessSemaphore 는 프로세스만을 위한 동기화 object. 리눅스에서는 프로세스
|
|
동기화에 Semaphore 만 쓸 수 있고, Window 에서는 프로세스 동기화에 Mutex 도 쓸 수
|
|
있다. 리눅스에서는 프로세스 동기화에 쓰이는 Semaphore 와 스레드 동기화에 쓰이는
|
|
세마포어가 함수와 용법이 다르다.
|
|
|
|
■ 시스템 호출 : semget() (SYSTEM CALL:semget())
|
|
|
|
새로운 세마퍼 집합을 만들거나 존재하는 집합에 접근하기 위하여, semget() 시스템 호출
|
|
을 사용한다.
|
|
|
|
SYSTEM CALL: semget();
|
|
|
|
PROTOTYPE: int semget ( key_t key, int nsems, int semflg );
|
|
RETURNS: semaphore set IPC identifier on success
|
|
-1 on error: errno = EACCESS (permission denied)
|
|
EEXIST (set exists, cannot create (IPC_EXCL))
|
|
EIDRM (set is marked for deletion)
|
|
ENOENT (set does not exist, no IPC_CREAT was used)
|
|
ENOMEM (Not enough memory to create new set)
|
|
ENOSPC (Maximum set limit exceeded)
|
|
□ NOTES:
|
|
|
|
semget()의 첫번째 아규먼트는 ftok()의 호출에 의해 반환된 키값이다.
|
|
이 키값은 커널안에 존재하는 다른 세마퍼 집합의 키값과 비교된다. 이때 열거나 접근하는 동작은 semflg 아규먼트의 내용에 따라 결정된다.
|
|
|
|
IPC_CREAT
|
|
커널안에 이미 존재하지 않는다면 세마퍼 집합을 만든다.
|
|
IPC_EXCL
|
|
IPC_CREAT와 같이 사용되어, 세마퍼가 이미 존재하면 실패한다.
|
|
|
|
IPC_CREAT가 혼자 사용된다면, semget()은 새로 만들어진 집합의 세마퍼 집합 확인자
|
|
(the semaphore set identifier)를 반환하거나 같은 키값을 가지고 이미 존재하는
|
|
집합의 확인자를 반환한다. IPC_EXCL가 IPC_CREAT와 함께 쓰였다면,
|
|
새 집합을 만들거나 이미 집합이 존재한다면 -1 값을 가지고 호출에 실패한다.
|
|
IPC_EXCL은 그 자체로는 의미가 없지만, IPC_CREAT와 조합되어 쓰여질 때
|
|
존재하지않는 세마퍼를 접근(access)하기 위해 열려지는 것을 막는 장치로 사용될 수 있다.
|
|
시스템 V IPC의 다른 형태들처럼 부가적인 8진 모드가 세마퍼 집합의 허가사항 형태에 마스크로 OR될 수 있다.
|
|
|
|
nsems 아규먼트는 새 집합안에서 만들어져야 하는 세마퍼의 갯수를 지정한다.
|
|
이것은 앞에서 설명했던 가상의 인쇄방의 프린터의 갯수를 의미한다.
|
|
집합안의 최대 세마퍼의 갯수는 linux/sem.h에 정의 되어있다.
|
|
|
|
#define SEMMSL 32 // <=512 id당 세마퍼의 최대 갯수
|
|
|
|
이미 존재하는 집합을 명확히 열고 있다면, nsems 아규먼트는 무시됨에 주목해라.
|
|
세마퍼 집합을 만들고 여는 wrapper 함수를 만들어 보자:
|
|
|
|
int open_semaphore_set( key_t keyval, int numsems )
|
|
{
|
|
int sid;
|
|
|
|
if ( ! numsems )
|
|
return(-1);
|
|
if((sid = semget( mykey, numsems, IPC_CREAT | 0660 )) == -1)
|
|
return(-1);
|
|
//endif
|
|
|
|
return(sid);
|
|
}
|
|
//int open_semaphore_set( key_t keyval, int numsems )
|
|
|
|
0600의 명시적인 허가사항을 사용함에 주의해라.
|
|
이 작은 함수는 세마퍼 집합 확인자(int)를 반환하거나 에러시 -1을 반환한다.
|
|
새 집합이 만들어졌다면, 공간을 할당하기 위해 세마퍼의 갯수와 마찬가지로 키값이 넘겨져야 한다.
|
|
이장의 끝에서 표현될 예제에서, 세마퍼 집합이 존재하는지 존재하지 않는지를
|
|
결정하기 위한 IPC_EXCL 플래그의 사용에 주목해라
|
|
|
|
□ -- NOTES --
|
|
|
|
■ int semctl ( int semid, int semnum, int cmd, union semun arg );
|
|
|
|
-1 on error: errno = EACCESS (permission denied)
|
|
EFAULT(invalid address pointed to by arg argument)
|
|
EIDRM (semaphore set was removed)
|
|
EINVAL(set doesn't exist, or semid is invalid)
|
|
EPERM (EUID has no privileges for cmd in arg)
|
|
ERANGE(semaphore value out of range)
|
|
|
|
□ semctl() 함수의 3 번째 인수가 가질 수 있는 여러 값
|
|
|
|
IPC_STAT
|
|
집합에 대한 semid_ds 구조를 조회하고,
|
|
semun union안의 buf 아규먼트의 주소지에 저장한다.
|
|
|
|
IPC_SET
|
|
집합에 대한 semid_ds 구조의 ipc_perm 멤버의 값을 지정한다.
|
|
semum union의 buf 아규먼트로 부터 값을 가져온다.
|
|
|
|
IPC_RMID
|
|
커널로 부터 집합을 제거한다.
|
|
|
|
GETALL
|
|
집합으로 부터 모든 세마퍼의 값을 얻는데 사용된다.
|
|
정수값들이 union의 배열 멤버에 의해 지정된 unsigned short integer 배열에 저장된다.
|
|
|
|
GETCNT
|
|
자원에 대해 현재 기다리고 있는 프로세스의 수를 반환한다.
|
|
|
|
GETPID
|
|
마지막 semop 호출을 수행한 프로세스의 PID를 반환한다.
|
|
|
|
GETZCNT
|
|
100% 자원 활용을 위해 현재 기다리고 있는 프로세스의 수를 반환한다.
|
|
|
|
SETALL
|
|
집합안의 모든 세마퍼의 값을 union의 배열 멤버안에 포함된 매칭되는 값으로 지정한다.
|
|
|
|
SETVAL
|
|
집합안의 개별적인 세마퍼의 값을 union의 val 멤버의 값으로 지정한다.
|
|
|
|
■ arg 아규먼트는 semun 타입의 예를 나타낸다.
|
|
이 특별한 연합체(union)는 linux/sem.h에 다음과 같이 선언되어있다.
|
|
|
|
union semun
|
|
{
|
|
int val; // SETVAL을 위한 값
|
|
struct semid_ds *buf; // IPC_STAT & IPC_SET을 위한 버퍼
|
|
ushort *array; // GETALL & SETALL를 위한 배열
|
|
struct seminfo *__buf; // IPC_INFO를 위한 버퍼
|
|
void *__pad;
|
|
};
|
|
|
|
val
|
|
SETVAL 명령어가 수행될 때 사용된다.세마퍼에 지정될 값을 지정한다.
|
|
|
|
buf
|
|
IPC_STAT/IPC_SET에서 사용된다.
|
|
커널안에서 사용되는 내부 세마퍼 자료 구조의 복사본을 나타낸다.
|
|
|
|
array
|
|
GETALL/SETALL 명령어에서 사용되는 포인터.
|
|
집합안에서 모든 세마퍼 값들을 조회하거나 지정하는데 사용되는
|
|
정수값들의 배열을 가리키고 있어야 한다.
|
|
남아있는 아규먼트인 _buf와 _pad는 커널 안의 세마퍼 코드내에서
|
|
내부적으로 사용되며 응용프로그램 개발자에게는 거의 쓸모가 없다.
|
|
사실상, 이런 두개의 아규먼트는 리눅스 운영체제에서 지정되며
|
|
다른 유닉스 구현에서는 찾을 수 없다.
|
|
이런 특별한 시스템 호출을 모든 시스템 V IPC 호출을 이해하는데
|
|
가장 어려운 점으로 꼽을 수 있으므로,실제로 이러한 다양한 예를 검사할 것이다.
|
|
|
|
마지막 아규먼트(union)은 GETVAL 명령어가 사용될 때 무시된다.
|
|
|
|
■ Posix 형 Semaphore 는 unistd.h 헤더 파일에 _POSIX_SEMAPHORES 옵션이
|
|
정의되었으면 사용할 수 있다.
|
|
|
|
-- POSIX(포직스) 쓰레드를 이용한 프로그래밍 (2011-05-23 20:31:00)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
class ZCProcessSemaphore
|
|
{
|
|
private:
|
|
ZTypeID mh_Semaphore;
|
|
public :
|
|
|
|
ZCProcessSemaphore()
|
|
{
|
|
mh_Semaphore=0;
|
|
}/*
|
|
ZCProcessSemaphore()*/
|
|
|
|
// Linux 에서는 세마포어를 배열로 선언할 수 있으나
|
|
// 거의 쓸 일이 없고 원소 하나의 배열로 사용한다.
|
|
|
|
bool Make(const char* AP_SemaName, long AL_InitialCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return this->Make(ZNsMain::ZfGetKeyNum(AP_SemaName), AL_InitialCnt, AI_Flag);
|
|
}/*
|
|
bool Make(const char* AP_SemaName, long AL_InitialCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool Make(long AL_KeyID,int AL_InitialCnt,int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
if((mh_Semaphore=semget((key_t)AL_KeyID,1,AI_Flag))==-1)
|
|
{
|
|
if(errno==EEXIST)
|
|
return (mh_Semaphore=semget((key_t)AL_KeyID,1,0))!=-1 ;
|
|
return false;
|
|
}/*
|
|
if((mh_Semaphore=semget((key_t)AL_KeyID,1,AI_Flag))==-1)*/
|
|
|
|
union semun SemUnion; SemUnion.val=AL_InitialCnt;
|
|
|
|
return semctl(mh_Semaphore, 0, SETVAL,SemUnion) != -1 ;
|
|
}/*
|
|
bool Make(long AL_KeyID,int AL_InitialCnt,int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
|
|
// 아래 함수는 뮤텍스처럼 사용하는데 좋다.
|
|
|
|
bool Make(int AL_InitialCnt=1, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return Make(this->GetUniqueSemaKey(), AL_InitialCnt, AI_Flag);
|
|
}/*
|
|
bool Make(int AL_InitialCnt=1, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
|
|
// 비신호상태의 세마포어를 만든다.
|
|
|
|
bool MakeZero(int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return Make(this->GetUniqueSemaKey(), 0, AI_Flag);
|
|
}/*
|
|
bool MakeZero(int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
|
|
static long GetUniqueSemaKey()
|
|
{
|
|
static long SL_UniqueID=0 ;
|
|
const long CL_AddID =100000;
|
|
|
|
return ( SL_UniqueID += CL_AddID ) + ::getpid() ;
|
|
}/*
|
|
static long GetUniqueSemaKey()*/
|
|
|
|
|
|
bool MakeArr(long AL_KeyID, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
if((mh_Semaphore=semget((key_t)AL_KeyID, AI_SemaCnt, AI_Flag))==-1)
|
|
{
|
|
if(errno==EEXIST)
|
|
return (mh_Semaphore=semget((key_t)AL_KeyID, AI_SemaCnt,0))!=-1 ;
|
|
|
|
return false;
|
|
}/*
|
|
if((mh_Semaphore=semget((key_t)AL_KeyID, AI_SemaCnt, AI_Flag))==-1)*/
|
|
|
|
union semun SemUnion; SemUnion.val=AL_InitialCnt;
|
|
|
|
// 초기화는 SETALL 옵션을 사용해도 되는데 SemUnion.array 에 배열을 할당하고
|
|
// 초기화하고 대입하는 과정을 생각하면 그냥 for 문으로도 상관없을 것 같다.
|
|
|
|
__for0(ZTypInt, i, AI_SemaCnt)
|
|
{
|
|
if(semctl(mh_Semaphore, i, SETVAL,SemUnion) != -1 ) return false;
|
|
}/*
|
|
__for0(ZTypInt, i, AI_SemaCnt)*/
|
|
|
|
return true;
|
|
}/*
|
|
bool MakeArr(long AL_KeyID, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool MakeArr(const char* AP_SemaName, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return MakeArr(ZNsMain::ZfGetKeyNum(AP_SemaName), AL_InitialCnt, AI_SemaCnt, AI_Flag);
|
|
}/*
|
|
bool MakeArr(const char* AP_SemaName, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
|
|
// bool MakeStd() 는 class ZCProcessSemaphore 가 Window, Linux 양쪽에서 호환하기 위한 것,
|
|
// 인수 AI_MaxSemaCnt 는 사용하지 않고 있다.(Window 에서만 의미가 있다.)
|
|
|
|
bool MakeStd(const char* AP_SemaName, int AL_InitialCnt, int AI_MaxSemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
|
|
{
|
|
return this->Make(ZNsMain::ZfGetKeyNum(AP_SemaName), AL_InitialCnt, AI_Flag);
|
|
}/*
|
|
bool MakeStd(const char* AP_SemaName, int AL_InitialCnt, int AI_MaxSemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
|
|
|
|
bool Open(const char* AP_SemaName)
|
|
{
|
|
return (mh_Semaphore=semget((key_t)(ZNsMain::ZfGetKeyNum(AP_SemaName)), 1, 0))>0 ;
|
|
}/*
|
|
bool Open(const char* AP_SemaName)*/
|
|
|
|
|
|
// 현재 세마포어의 값을 가져온다.
|
|
|
|
int GetValue(int AI_SemaNum=0)
|
|
{
|
|
return semctl(mh_Semaphore, AI_SemaNum, GETVAL, 0/*union semun arg*/) ;
|
|
}/*
|
|
int GetValue(int AI_SemaNum=0)*/
|
|
|
|
bool GetValue(union semun& ARR_SemUnion)
|
|
{
|
|
return semctl(mh_Semaphore, 0/*ignored*/, GETALL, ARR_SemUnion) !=-1 ;
|
|
}/*
|
|
bool GetValue(union semun& ARR_SemUnion)*/
|
|
|
|
bool IsZeroCount()
|
|
{
|
|
return this->GetValue()==0;
|
|
}/*
|
|
bool IsZeroCount()*/
|
|
|
|
bool Lock(ZTypUInt AI_ReleaseCount=1)
|
|
{
|
|
return LockRaw(AI_ReleaseCount)!=-1;
|
|
}/*
|
|
bool Lock(ZTypUInt AI_ReleaseCount=1)*/
|
|
|
|
|
|
// LockRaw() 는 성공이면 ZEThread_OK, 시간 초과는 ZEThread_TimeOut 을 반환한다.
|
|
// 리눅스의 Posix 세마포어에서도 같다.
|
|
|
|
int LockRaw(ZTypUInt AI_ReleaseCount=1)
|
|
{
|
|
// cf) Window 의 ReleaseSemaphore(mh_Semaphore,lReleaseCount,lpPreviousCount) 에서
|
|
// 두번째 인수의 역할을 AI_ReleaseCount 가 한다.
|
|
|
|
struct sembuf sem_b;
|
|
|
|
sem_b.sem_num= 0 ;
|
|
sem_b.sem_op =-AI_ReleaseCount;
|
|
sem_b.sem_flg= SEM_UNDO ;
|
|
|
|
return semop(mh_Semaphore, &sem_b,1) ;
|
|
}/*
|
|
int LockRaw(ZTypUInt AI_ReleaseCount=1)*/
|
|
|
|
bool UnLock(ZTypUInt AI_ReleaseCount=1)
|
|
{
|
|
// cf) Window 의 ReleaseSemaphore(mh_Semaphore,lReleaseCount,lpPreviousCount) 에서
|
|
// 두번째 인수의 역할을 AI_ReleaseCount 가 한다.
|
|
|
|
struct sembuf sem_b;
|
|
|
|
sem_b.sem_num=0;
|
|
sem_b.sem_op =AI_ReleaseCount;
|
|
sem_b.sem_flg=SEM_UNDO;
|
|
|
|
return semop(mh_Semaphore, &sem_b,1)!=-1 ;
|
|
}/*
|
|
bool UnLock(ZTypUInt AI_ReleaseCount=1)*/
|
|
|
|
bool Close()
|
|
{
|
|
union semun SemUnion; bool VB_IsOK =
|
|
(semctl(mh_Semaphore, 0, IPC_RMID ,SemUnion)!=-1) ;
|
|
|
|
mh_Semaphore=0; return VB_IsOK;
|
|
}/*
|
|
bool Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCProcessSemaphore*/
|
|
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
/////////// end class ZCProcessMutex ///////////
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
|
|
/*/////////////////////////////////////////////
|
|
|
|
#define SEM_FAILED 0
|
|
#define SEM_VALUE_MAX 1147483648
|
|
|
|
int sem_init (sem_t * sem, int pshared, unsigned int value);
|
|
int sem_destroy (sem_t * sem);
|
|
sem_t *sem_open (const char *name, int oflag, ...);
|
|
int sem_close (sem_t *sem);
|
|
int sem_wait (sem_t * sem);
|
|
int sem_trywait (sem_t * sem);
|
|
int sem_timedwait (sem_t * sem, const struct timespec *abstime);
|
|
int sem_post (sem_t * sem);
|
|
int sem_getvalue(sem_t * sem, int *sval);
|
|
|
|
|
|
cf)
|
|
|
|
#include <semaphore.h>
|
|
|
|
int sem_wait(sem_t *sem);
|
|
int sem_trywait(sem_t *sem);
|
|
|
|
DESCRIPTION
|
|
The sem_wait() function locks the semaphore referenced by sem
|
|
by performing a semaphore lock operation on that semaphore.
|
|
If the semaphore value is currently zero,
|
|
then the calling thread will not return from the call to sem_wait()
|
|
until it either locks the semaphore or the call is interrupted by a signal.
|
|
The sem_trywait() function locks the semaphore referenced by sem
|
|
only if the semaphore is currently not locked;
|
|
that is, if the semaphore value is currently positive.
|
|
Otherwise, it does not lock the semaphore.
|
|
Upon successful return,
|
|
the state of the semaphore is locked and remains locked
|
|
until the sem_post() function is executed and returns successfully.
|
|
|
|
The sem_wait() function is interruptible by the delivery of a signal.
|
|
|
|
|
|
|
|
HMUG - Mac OS X / Darwin man pages
|
|
Current Directory /man/2
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
NAME
|
|
|
|
sem_open - initialize and open a named semaphore
|
|
|
|
SYNOPSIS
|
|
|
|
#include <semaphore.h>
|
|
|
|
sem_t* sem_open(const char *name, int flags)
|
|
sem_t* sem_open(const char *name, int flags, mode_t mode, unsigned int value)
|
|
|
|
DESCRIPTION
|
|
The named semaphore named name is initialized and opened as specified by
|
|
the argument flags and a semaphore descriptor is returned to the calling
|
|
process.
|
|
|
|
The flags specified are formed by or'ing the following values:
|
|
|
|
O_CREAT create the semaphore if it does not exist
|
|
O_EXCL error if create and semaphore exists
|
|
|
|
If O_CREATE if specified, sem_open() requires an additional two argu-
|
|
ments. mode specifies the permissions for the semaphore as described in
|
|
chmod(2) and modified by the process' umask value (see umask(2)). The
|
|
semaphore is created with an initial value, which must be less than or
|
|
equal to SEM_VALUE_MAX.
|
|
|
|
If O_EXCL is specified and the semaphore exists, sem_open() fails. The
|
|
check for the existence of the semaphore and the creation of the
|
|
semaphore are atomic with respect to all processes calling sem_open()
|
|
with O_CREAT and O_EXCL set.
|
|
|
|
When a new semaphore is created, it is given the user ID and group ID
|
|
which coorespond to the effective user and group IDs of the calling pro-
|
|
cess. There is no visible entry in the file system for the created object
|
|
in this implementation.
|
|
|
|
The returned semaphore descriptor is available to the calling process un-
|
|
til it is closed with sem_close(), or until the caller exits or execs.
|
|
|
|
If a process makes repeated calls to sem_open(), with the same name argu-
|
|
ment, the same descriptor is returned for each successful call, unless
|
|
sem_unlink() has been called on the semaphore in the interim.
|
|
|
|
If sem_open() fails for any reason, it will return a value of SEM_FAILED
|
|
and sets errno. On success, it returns a semaphore descriptor.
|
|
|
|
|
|
ERRORS
|
|
|
|
The named semaphore is opened unless:
|
|
|
|
[EACCES] The required permissions (for reading and/or writing) are
|
|
denied for the given flags; or O_CREAT is specified, the
|
|
object does not exist, and permission to create the
|
|
semaphore is denied.
|
|
|
|
[EEXIST] O_CREAT and O_EXCL were specified and the semaphore exists.
|
|
|
|
[EINTR] The sem_open() operation was interrupted by a signal.
|
|
|
|
[EINVAL] The shm_open() operation is not supported; or O_CREAT is
|
|
specified and value exceeds SEM_VALUE_MAX.
|
|
|
|
[EMFILE] The process has already reached its limit for semaphores or
|
|
file descriptors in use.
|
|
|
|
[ENAMETOOLONG]
|
|
name exceeded SEM_NAME_LEN characters.
|
|
|
|
[ENFILE] Too many semaphores or file descriptors are open on the
|
|
system.
|
|
|
|
[ENOENT] O_CREAT is not set and the named semaphore does not exist.
|
|
|
|
[ENOSPC] O_CREAT is specified, the file does not exist, and there is
|
|
insufficient space available to create the semaphore.
|
|
|
|
SEE ALSO
|
|
semctl(2), semget(2), semop(2), sem_close(2), sem_post(2),
|
|
sem_trywait(2), sem_unlink(2), sem_wait(2), umask(2)
|
|
|
|
HISTORY
|
|
sem_open() is specified in the POSIX Realtime Extension
|
|
(1003.1b-1993/1003.1i-1995).
|
|
|
|
Darwin Operating System June 8, 2000 2
|
|
|
|
/////////////////////////////////////////////*/
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////
|
|
|
|
■ class ZCThreadSemaphore 는 윈도우와 상당히 호환되기 어렵다.
|
|
윈도우와 호환은 class ZCProcessSemaphore 이 약간 좋다.
|
|
|
|
■ sem_t 에는 0 을 대입할 수 없다.
|
|
|
|
//////////////////////////////////////////////////////////*/
|
|
|
|
class ZCThreadSemaphore
|
|
{
|
|
private:
|
|
sem_t mh_Semaphore;
|
|
public :
|
|
|
|
/*/////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ sem_init() 함수는 이름없는 세마포어를 만들때 사용한다. 이름있는 세마포어의
|
|
경우 sem_open() 을 사용해야 하며, 파괴할 때도 sem_close() 와 sem_unlink() 를
|
|
사용해야 한다. 이 클래스에서는 이름없는 세마포어만 다룬다.
|
|
|
|
AI_Shared 가 0 이 아니면 다중 프로세스에서도 사용할 수 있으나 현재 Linux 의
|
|
sem_XXX 계열의 함수는 이 기능을 지원하지 않고 있다.(2004.7.12)
|
|
|
|
■ AI_InitValue==1 이라면 뮤텍스처럼 사용하려는 경우이다. 2007-09-30 14:32:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
bool Make(int AI_InitValue=1,int AI_Shared=0)
|
|
{
|
|
return ::sem_init(&mh_Semaphore, AI_Shared, AI_InitValue) !=-1 ;
|
|
}/*
|
|
bool Make(int AI_InitValue=1,int AI_Shared=0);*/
|
|
|
|
bool MakeZero(int AI_Shared=0)
|
|
{
|
|
return ::sem_init(&mh_Semaphore,AI_Shared,0) !=-1 ;
|
|
}/*
|
|
bool MakeZero(int AI_Shared=0)*/
|
|
|
|
bool Lock()
|
|
{
|
|
return ::sem_wait(&mh_Semaphore)==0 ;
|
|
}/*
|
|
bool Lock()*/
|
|
|
|
int LockRaw()
|
|
{
|
|
return ::sem_wait(&mh_Semaphore);
|
|
}/*
|
|
int LockRaw()*/
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
// cf) int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
|
|
|
|
int LockRaw(const struct timespec& AR_TimeOut)
|
|
{
|
|
// timeout 이면 errno 가 ZNsEnum::ZEThread_TimeOut
|
|
|
|
if(::sem_timedwait(&mh_Semaphore, &AR_TimeOut)!=ZNsEnum::ZEThread_OK)
|
|
return errno;
|
|
//endif
|
|
|
|
return ZNsEnum::ZEThread_OK;
|
|
}/*
|
|
int Lock(const struct timespec& AR_TimeOut)*/
|
|
|
|
int LockRaw(long AL_MiliSec)
|
|
{
|
|
timespec VO_TimeSpec;
|
|
|
|
VO_TimeSpec.tv_sec =time(NULL)+AL_MiliSec/1000;
|
|
VO_TimeSpec.tv_nsec=(AL_MiliSec%1000)*1000*1000;
|
|
|
|
return ::sem_timedwait(&mh_Semaphore,&VO_TimeSpec);
|
|
}/*
|
|
LockRaw LockRaw(long AL_MiliSec)*/
|
|
|
|
int LockTime(long AL_MiliSec)
|
|
{
|
|
return LockRaw(AL_MiliSec);
|
|
}/*
|
|
int LockTime(long AL_MiliSec)*/
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
// sem_trywait() 은 이미 lock 이 걸려 있으면 -1 을
|
|
// 반환하고, errno 가 EAGAIN 으로 셋팅된다.
|
|
|
|
bool TryLock()
|
|
{
|
|
return ::sem_trywait(&mh_Semaphore)==0 ;
|
|
}/*
|
|
bool TryLock()*/
|
|
|
|
bool UnLock()
|
|
{
|
|
return ::sem_post(&mh_Semaphore)==0 ;
|
|
}/*
|
|
bool UnLock()*/
|
|
|
|
|
|
// sem_destroy() 함수는 이름없는 세마포어의 제거에만 사용되는 함수다.
|
|
// 이름있는 세마포어에 사용할 경우에 그 행위는 정의되어 있지 않다.
|
|
|
|
bool Close()
|
|
{
|
|
return (sem_destroy(&mh_Semaphore)==0);
|
|
}/*
|
|
bool Close()*/
|
|
|
|
int GetValue() const
|
|
{
|
|
int VI_Val=0; const bool CB_IsOK =
|
|
(::sem_getvalue((sem_t*)&mh_Semaphore, &VI_Val)!=0);
|
|
|
|
if(CB_IsOK) return -1; return VI_Val;
|
|
}/*
|
|
int GetValue() const*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCThreadSemaphore*/
|
|
|
|
|
|
|
|
// 리눅스 쓰레드 뮤텍스는 복사해서 사용할 수 없다.
|
|
|
|
class ZCThreadMutex
|
|
{
|
|
private :
|
|
|
|
ZCThreadMutex(const ZCThreadMutex& rhs){}
|
|
|
|
ZCThreadMutex& operator=(const ZCThreadMutex& rhs)
|
|
{
|
|
return *this;
|
|
}/*
|
|
ZCThreadMutex& operator=(const ZCThreadMutex& rhs)*/
|
|
|
|
/*private :*/
|
|
protected:
|
|
pthread_mutex_t mo_Mutex;
|
|
public :
|
|
|
|
ZCThreadMutex()
|
|
{
|
|
}/*
|
|
ZCThreadMutex()*/
|
|
|
|
const pthread_mutex_t& GetMutexID() const
|
|
{
|
|
return mo_Mutex;
|
|
}/*
|
|
const pthread_mutex_t& GetMutexID() const*/
|
|
|
|
pthread_mutex_t& GetMutexID()
|
|
{
|
|
return mo_Mutex;
|
|
}/*
|
|
pthread_mutex_t& GetMutexID()*/
|
|
|
|
bool Make(const pthread_mutexattr_t* AP_MutexAttr=0)
|
|
{
|
|
return pthread_mutex_init(&mo_Mutex, AP_MutexAttr)==0 ;
|
|
}/*
|
|
bool Make(const pthread_mutexattr_t* AP_MutexAttr=0)*/
|
|
|
|
bool Lock()
|
|
{
|
|
return this->LockRaw()==0 ;
|
|
}/*
|
|
bool Lock()*/
|
|
|
|
int LockRaw()
|
|
{
|
|
return pthread_mutex_lock(&mo_Mutex) ;
|
|
}/*
|
|
int LockRaw()*/
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
bool Lock(long AL_MiliSec)
|
|
{
|
|
return this->LockRaw(AL_MiliSec)==0 ;
|
|
}/*
|
|
bool Lock(long AL_MiliSec)*/
|
|
|
|
|
|
// cf) 30 초간 LOCK
|
|
//
|
|
// struct timespec ts_timeout;
|
|
// ts_timeout.tv_sec =time(NULL)+30;
|
|
// ts_timeout.tv_nsec=0;
|
|
|
|
int LockRaw(const struct timespec& AR_TimeOut)
|
|
{
|
|
return pthread_mutex_timedlock(&mo_Mutex, &AR_TimeOut) ;
|
|
}/*
|
|
int LockRaw(const struct timespec& AR_TimeOut)*/
|
|
|
|
int LockRaw(long AL_MiliSec)
|
|
{
|
|
timespec VO_TimeSpec;
|
|
|
|
VO_TimeSpec.tv_sec = time(NULL)+AL_MiliSec/1000 ;
|
|
VO_TimeSpec.tv_nsec= (AL_MiliSec%1000)*1000*1000 ;
|
|
|
|
return pthread_mutex_timedlock(&mo_Mutex, &VO_TimeSpec) ;
|
|
}/*
|
|
int LockRaw(long AL_MiliSec)*/
|
|
|
|
int LockTime(long AL_MiliSec)
|
|
{
|
|
return LockRaw(AL_MiliSec);
|
|
}/*
|
|
int LockTime(long AL_MiliSec)*/
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
int TryLock()
|
|
{
|
|
return pthread_mutex_trylock(&mo_Mutex);
|
|
}/*
|
|
int TryLock()*/
|
|
|
|
bool UnLock()
|
|
{
|
|
return pthread_mutex_unlock(&mo_Mutex)==0 ;
|
|
}/*
|
|
bool UnLock()*/
|
|
|
|
bool Close()
|
|
{
|
|
return pthread_mutex_destroy(&mo_Mutex)==0 ;
|
|
}/*
|
|
bool Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCThreadMutex*/
|
|
|
|
|
|
class ZCThreadMutexAttr
|
|
{
|
|
private:
|
|
|
|
ZCThreadMutexAttr(const ZCThreadMutexAttr& rhs)
|
|
{
|
|
}/*
|
|
ZCThreadMutexAttr(const ZCThreadMutexAttr& rhs)*/
|
|
|
|
ZCThreadMutexAttr& operator=(const ZCThreadMutexAttr& rhs)
|
|
{
|
|
return *this;
|
|
}/*
|
|
ZCThreadMutexAttr& operator=(const ZCThreadMutexAttr& rhs)*/
|
|
|
|
/*private :*/
|
|
protected:
|
|
|
|
pthread_mutex_t mo_Mutex ;
|
|
pthread_mutexattr_t mo_PThreadMutexAttr;
|
|
|
|
public :
|
|
|
|
ZCThreadMutexAttr()
|
|
{
|
|
}/*
|
|
ZCThreadMutexAttr()*/
|
|
|
|
const pthread_mutex_t& GetMutexID() const
|
|
{
|
|
return mo_Mutex;
|
|
}/*
|
|
const pthread_mutex_t& GetMutexID() const*/
|
|
|
|
pthread_mutex_t& GetMutexID()
|
|
{
|
|
return mo_Mutex;
|
|
}/*
|
|
pthread_mutex_t& GetMutexID()*/
|
|
|
|
bool Make(int AI_ThreadType=PTHREAD_MUTEX_RECURSIVE)
|
|
{
|
|
// PTHREAD_MUTEX_RECURSIVE mutex 는 같은 쓰레드에서 소유한 만큼 해제해야한다.
|
|
// 따라서 클래스 생성자에서 소유하고 소멸자에서 해제하기에 딱 좋다.
|
|
|
|
if(pthread_mutexattr_init(&mo_PThreadMutexAttr)!=0)
|
|
return false;
|
|
if(pthread_mutexattr_settype(&mo_PThreadMutexAttr,AI_ThreadType)!=0)
|
|
return false;
|
|
if(pthread_mutexattr_setpshared(&mo_PThreadMutexAttr,PTHREAD_PROCESS_SHARED)!=0)
|
|
return false;
|
|
//endif
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
|
|
if(pthread_mutexattr_setpshared(&mo_PThreadMutexAttr,PTHREAD_PROCESS_PRIVATE)!=0)
|
|
return false;
|
|
//endif
|
|
|
|
도 가능한데, Mutex 를 프로세스 사이에 공유되게 하고 싶다면 PTHREAD_PROCESS_SHARED
|
|
을 기본값으로 하는 것이 맞다.
|
|
|
|
-- 2010-01-04 11:59:00
|
|
|
|
///////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
return pthread_mutex_init(&mo_Mutex,(const pthread_mutexattr_t*)&mo_PThreadMutexAttr)==0 ;
|
|
}/*
|
|
bool Make(int AI_ThreadType=PTHREAD_MUTEX_RECURSIVE)*/
|
|
|
|
bool Make(const pthread_mutexattr_t* AP_MutexAttr)
|
|
{
|
|
return pthread_mutex_init(&mo_Mutex, AP_MutexAttr)==0 ;
|
|
}/*
|
|
bool Make(const pthread_mutexattr_t* AP_MutexAttr)*/
|
|
|
|
bool Lock()
|
|
{
|
|
return this->LockRaw()==0 ;
|
|
}/*
|
|
bool Lock()*/
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
bool Lock(long AL_MiliSec)
|
|
{
|
|
return this->LockRaw(AL_MiliSec)==0 ;
|
|
}/*
|
|
bool Lock(long AL_MiliSec)*/
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
int LockRaw()
|
|
{
|
|
return pthread_mutex_lock(&mo_Mutex) ;
|
|
}/*
|
|
int LockRaw()*/
|
|
|
|
|
|
// cf) 30 초간 LOCK
|
|
//
|
|
// struct timespec ts_timeout;
|
|
// ts_timeout.tv_sec=time(NULL)+30;
|
|
// ts_timeout.tv_nsec=0;
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
int LockRaw(const struct timespec& AR_TimeOut)
|
|
{
|
|
return pthread_mutex_timedlock(&mo_Mutex, &AR_TimeOut) ;
|
|
}/*
|
|
int LockRaw(const struct timespec& AR_TimeOut)*/
|
|
|
|
int LockRaw(long AL_MiliSec)
|
|
{
|
|
timespec VO_TimeSpec;
|
|
|
|
VO_TimeSpec.tv_sec = time(NULL)+AL_MiliSec/1000 ;
|
|
VO_TimeSpec.tv_nsec= (AL_MiliSec%1000)*1000*1000 ;
|
|
|
|
return pthread_mutex_timedlock(&mo_Mutex, &VO_TimeSpec) ;
|
|
}/*
|
|
int LockRaw(long AL_MiliSec)*/
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
int TryLock()
|
|
{
|
|
return pthread_mutex_trylock(&mo_Mutex);
|
|
}/*
|
|
int TryLock()*/
|
|
|
|
bool UnLock()
|
|
{
|
|
return pthread_mutex_unlock(&mo_Mutex)==0 ;
|
|
}/*
|
|
bool UnLock()*/
|
|
|
|
bool Close()
|
|
{
|
|
return pthread_mutexattr_destroy(&mo_PThreadMutexAttr)==0 &&
|
|
pthread_mutex_destroy (&mo_Mutex) ==0 ;
|
|
///////
|
|
}/*
|
|
bool Close()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCThreadMutexAttr*/
|
|
|
|
|
|
typedef ZCThreadMutexAttr ZCThreadMutexStd;
|
|
|
|
|
|
class ZCThreadMutexEasy : protected ZCThreadMutex
|
|
{
|
|
public:
|
|
|
|
ZCThreadMutexEasy()
|
|
{
|
|
if(this->ZCThreadMutex::Make()==false)
|
|
{
|
|
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
|
|
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
|
|
fileout<<"Error !! ZCThreadMutexEasy() Fail To Mutex"<<std::endl;
|
|
fileout.close();
|
|
exit(1);
|
|
}/*
|
|
if(this->ZCThreadMutex::Make()==false)*/
|
|
}/*
|
|
ZCThreadMutexEasy()*/
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 아래 선언
|
|
|
|
ZCThreadMutexEasy(const ZCThreadMutexEasy& rhs) : ZCThreadMutex()
|
|
|
|
에서 ZCThreadMutex() 를 빼면 g++ 4.4.6 에서 -W 옵션을 주면, 아래 경고 메시지를 볼 수 있다.
|
|
|
|
warning: base class 'class std::ZCThreadMutex' should be explicitly initialized in the copy constructor
|
|
|
|
위 경고 메시지가 합리적이라고 생각되는 것이, 컴파일러는 기초 클래스의 복사 생성자 ZCThreadMutex(rhs) 을 호출
|
|
할 것이라고 예상했기 때문일 것이다.
|
|
|
|
-- 2013-05-05 06:49:00
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
ZCThreadMutexEasy(const ZCThreadMutexEasy& rhs) : ZCThreadMutex()
|
|
{
|
|
if(this->ZCThreadMutex::Make()==false)
|
|
{
|
|
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
|
|
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
|
|
fileout<<"Error !! ZCThreadMutexEasy(const ZCThreadMutexEasy&) Fail To Mutex"<<std::endl;
|
|
fileout.close();
|
|
::exit(1);
|
|
}/*
|
|
if(this->ZCThreadMutex::Make()==false)*/
|
|
}/*
|
|
ZCThreadMutexEasy(const ZCThreadMutexEasy& rhs)*/
|
|
|
|
~ZCThreadMutexEasy()
|
|
{
|
|
if(this->ZCThreadMutex::Close()==false)
|
|
{
|
|
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
|
|
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
|
|
fileout<<"Error !! ~ZCThreadMutexEasy() Fail To Close Mutex"<<std::endl;
|
|
fileout.close();
|
|
::exit(1);
|
|
}/*
|
|
if(this->ZCThreadMutex::Close()==false)*/
|
|
}/*
|
|
~ZCThreadMutexEasy()*/
|
|
|
|
|
|
using ZCThreadMutex::Lock ;
|
|
using ZCThreadMutex::UnLock;
|
|
|
|
public:
|
|
};/*
|
|
class ZCThreadMutexEasy*/
|
|
|
|
|
|
class ZCThreadMutexAttrEasy : protected ZCThreadMutexAttr
|
|
{
|
|
public:
|
|
|
|
ZCThreadMutexAttrEasy()
|
|
{
|
|
if(this->ZCThreadMutexAttr::Make()==false)
|
|
{
|
|
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
|
|
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
|
|
fileout<<"Error !! ZCThreadMutexAttrEasy() Fail To Mutex"<<std::endl;
|
|
fileout.close();
|
|
exit(1);
|
|
}/*
|
|
if(this->ZCThreadMutexAttr::Make()==false)*/
|
|
}/*
|
|
ZCThreadMutexAttrEasy()*/
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 아래 선언
|
|
|
|
ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&) : ZCThreadMutexAttr()
|
|
|
|
에서 ZCThreadMutex() 를 빼면 g++ 4.4.6 에서 -W 옵션을 주면, 아래 경고 메시지를 볼 수 있다.
|
|
|
|
warning: base class 'class std::ZCThreadMutexAttr' should be explicitly initialized in the copy constructor
|
|
|
|
위 경고 메시지가 합리적이라고 생각되는 것이, 컴파일러는 기초 클래스의 복사 생성자 ZCThreadMutexAttr(rhs) 을
|
|
호출할 것이라고 예상했기 때문일 것이다.
|
|
|
|
-- 2013-05-05 06:49:00
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&) : ZCThreadMutexAttr()
|
|
{
|
|
if(this->ZCThreadMutexAttr::Make()==false)
|
|
{
|
|
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
|
|
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
|
|
fileout<<"Error !! ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&) Fail To Mutex"<<std::endl;
|
|
fileout.close();
|
|
exit(1);
|
|
}/*
|
|
if(this->ZCThreadMutexAttr::Make()==false)*/
|
|
}/*
|
|
ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&)*/
|
|
|
|
~ZCThreadMutexAttrEasy()
|
|
{
|
|
if(this->ZCThreadMutexAttr::Close()==false)
|
|
{
|
|
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
|
|
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
|
|
fileout<<"Error !! ~ZCThreadMutexAttrEasy() Fail To Close Mutex"<<std::endl;
|
|
fileout.close();
|
|
exit(1);
|
|
}/*
|
|
if(this->ZCThreadMutexAttr::Close()==false)*/
|
|
}/*
|
|
~ZCThreadMutexAttrEasy()*/
|
|
|
|
|
|
using ZCThreadMutexAttr::Lock ;
|
|
using ZCThreadMutexAttr::UnLock;
|
|
|
|
public:
|
|
};/*
|
|
class ZCThreadMutexAttrEasy*/
|
|
|
|
|
|
/*/////////////////////////////////////////////////////////
|
|
|
|
■ 아래 기본 뮤텍스 ZCThreadMutexEasy 은 재귀적으로 사용할
|
|
수 없어서(테스트해 보면 dead lock 이 발생한다) 다소 불
|
|
안하다.(Window Mutex 에 비해서)
|
|
|
|
typedef ZCThreadMutexEasy ZCDefLockEasy;
|
|
|
|
그래서 아래 선언으로 바꾼다. -- 2009-07-28 16:27:00
|
|
|
|
재귀적으로 Mutex 를 사용하는 상황을 만들지 말아야 한다.
|
|
|
|
-- 2013-07-22 12:28:00
|
|
|
|
■ freebsd 8.2 에서 테스트할 때, 자꾸
|
|
|
|
pthread_mutexattr_setpshared(
|
|
&mo_PThreadMutexAttr,PTHREAD_PROCESS_SHARED
|
|
)!=0
|
|
|
|
가 되는 현상이 있어서, ZCThreadMutexEasy 를 사용할 수 없
|
|
었다. 그래서 __MUTEX_EASY_DEFAULT_LOCK__ 가 정의되어 있
|
|
으면,
|
|
|
|
typedef ZCThreadMutexEasy ZCDefLockEasy;
|
|
|
|
선언을 사용하게 한다. -- 2011-05-19 22:00:00
|
|
|
|
기본적으로 ZCThreadMutexEasy 을 사용하고 ZCThreadMutexAttrEasy
|
|
은 __MUTEX_ATTR_EASY_DEFAULT_LOCK__ 이 정의되어 있을 때,
|
|
사용하는 것으로 한다. -- 2015-09-02 21:05:00
|
|
|
|
/////////////////////////////////////////////////////////*/
|
|
|
|
#ifdef __MUTEX_ATTR_EASY_DEFAULT_LOCK__
|
|
typedef ZCThreadMutexAttrEasy ZCDefLockEasy;
|
|
#else
|
|
typedef ZCThreadMutexEasy ZCDefLockEasy;
|
|
#endif
|
|
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
/*///////////////////////////////////////////////////////////////////////
|
|
|
|
■ spin lock 은 짧은 시간 동기화에는 가장 좋다. 그런데 일반적으로 동기화
|
|
에 뮤텍스를 사용하자. spin lock 은 context switching 에 부담이 되어서
|
|
자칫 반응속도가 느려질 수 있다. 심지어 몇 초씩 걸러서 사용못하는 경우
|
|
도 발생한다.
|
|
|
|
■ 뮤텍스가 아닌 spin lock 으로 동기화를 하는 경우에는 동기화 영역이 정말
|
|
짧은 수행시간을 가져야 한다. 동기화 영역에서 콘솔에 출력하는 등의 '긴
|
|
처리'가 있으면 아주 나쁘다. 멀티 쓰레드에서 spin lock 으로 동기화를 할
|
|
때, 위와 같은 다소 '긴 처리'를 할 경우, 작업 쓰레드가 컨텍스트 스위칭
|
|
을 하면서 동기화 영역이 끝나지 않을 수 있고 또 다른 작업 쓰레드의 동기
|
|
화 영역에서 cpu 가 spin 하고 있다면 동기화가 끝나는 시점이 늦어지고,
|
|
심지어는 dead lock 에 빠질 수도 있다.
|
|
|
|
-- 2008-03-20 11:27:00
|
|
|
|
///////////////////////////////////////////////////////////////////////*/
|
|
|
|
class ZCSpinLock
|
|
{
|
|
private:
|
|
pthread_spinlock_t mh_SpinLock;
|
|
public :
|
|
|
|
int Init(int AI_ShareMode=PTHREAD_PROCESS_PRIVATE)
|
|
{
|
|
// 프로세스간에 spin lock 을 공유한다면 PTHREAD_PROCESS_SHARED 를 준다.
|
|
// 그리고 pthread_spinlock_t 도 공유메모리에 있어야 한다. 절차가 복잡하다.
|
|
|
|
return pthread_spin_init(&mh_SpinLock,AI_ShareMode);
|
|
}/*
|
|
void Init(int AI_ShareMode=PTHREAD_PROCESS_PRIVATE)*/
|
|
|
|
int Close()
|
|
{
|
|
return pthread_spin_destroy(&mh_SpinLock);
|
|
}/*
|
|
void Close()*/
|
|
|
|
int Delete() // for windows compatability
|
|
{
|
|
return this->Close();
|
|
}/*
|
|
void Delete()*/
|
|
|
|
int Lock()
|
|
{
|
|
return pthread_spin_lock(&mh_SpinLock);
|
|
}/*
|
|
void Lock()*/
|
|
|
|
int TryLock()
|
|
{
|
|
return pthread_spin_trylock(&mh_SpinLock);
|
|
}/*
|
|
void TryLock()*/
|
|
|
|
int UnLock()
|
|
{
|
|
return pthread_spin_unlock(&mh_SpinLock);
|
|
}/*
|
|
void UnLock()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCSpinLock*/
|
|
|
|
|
|
class ZCSpinLockEasy : protected ZCSpinLock
|
|
{
|
|
public:
|
|
/*#######################################################################
|
|
|
|
■ 소멸자를 제외한 각 멤버 함수 선언에 throw(ZtCExceptBase<int, int>) 을
|
|
붙였는데, g++ 7.3.0 에서 경고가 나서 지웠다.
|
|
|
|
CProcess_Linux.H:2429:25: warning:
|
|
dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
|
|
ZCSpinLockEasy() throw(ZtCExceptBase<int, int>)
|
|
|
|
-- 2018-06-26 15:12:00
|
|
|
|
#######################################################################*/
|
|
|
|
ZCSpinLockEasy() /*throw(ZtCExceptBase<int, int>)*/
|
|
{
|
|
int VI_Return=this->Init();
|
|
|
|
if(VI_Return!=0)
|
|
{
|
|
throw ZtCExceptBase<int, int>(VI_Return);
|
|
}/*
|
|
if(VI_Return!=0)*/
|
|
}/*
|
|
ZCSpinLockEasy()*/
|
|
|
|
ZCSpinLockEasy(const ZCSpinLockEasy& rhs)
|
|
{
|
|
int VI_Return=this->Init();
|
|
|
|
if(VI_Return!=0)
|
|
{
|
|
throw ZtCExceptBase<int, int>(VI_Return);
|
|
}/*
|
|
if(VI_Return!=0)*/
|
|
}/*
|
|
ZCSpinLockEasy(const ZCSpinLockEasy& rhs)*/
|
|
|
|
void Lock()
|
|
{
|
|
int VI_Return=this->ZCSpinLock::Lock();
|
|
|
|
if(VI_Return!=0)
|
|
{
|
|
throw ZtCExceptBase<int, int>(VI_Return);
|
|
}/*
|
|
if(VI_Return!=0)*/
|
|
}/*
|
|
void Lock() throw(ZtCExceptBase<int, int>)*/
|
|
|
|
void UnLock()
|
|
{
|
|
int VI_Return=this->ZCSpinLock::UnLock();
|
|
|
|
if(VI_Return!=0)
|
|
{
|
|
throw ZtCExceptBase<int, int>(VI_Return);
|
|
}/*
|
|
if(VI_Return!=0)*/
|
|
}/*
|
|
void UnLock() throw(ZtCExceptBase<int, int>)*/
|
|
|
|
~ZCSpinLockEasy()
|
|
{
|
|
this->Delete();
|
|
}/*
|
|
ZCSpinLockEasy()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCSpinLockEasy : protected ZCSpinLock*/
|
|
|
|
|
|
typedef ZCSpinLock ZCCriticSect ;
|
|
typedef ZCSpinLockEasy ZCCriticSectEasy;
|
|
|
|
#else // !__USE_XOPEN2K
|
|
|
|
typedef ZCThreadMutex ZCCriticSect ;
|
|
typedef ZCThreadMutexEasy ZCCriticSectEasy;
|
|
|
|
#endif //!__USE_XOPEN2K
|
|
|
|
|
|
template<typename TAutoKey> class ZtCAutoKeyRev;
|
|
|
|
|
|
/*////////////////////////////////////////////////
|
|
|
|
■ class ZtCAutoKey<> : Lock 을 설정하고 해제한다.
|
|
|
|
열고 닫을 수 있는 열쇠와 같아 Key 라고 하였다.
|
|
|
|
--
|
|
|
|
이런 식의 기법을 RAII
|
|
|
|
Resource Acquisition Is Initialization
|
|
|
|
라고 한다는 것을 알았다. -- 2015-03-10 15:08:00
|
|
|
|
////////////////////////////////////////////////*/
|
|
|
|
template< typename TCriticSectEasy=ZCThreadMutexEasy
|
|
>
|
|
class ZtCAutoKey /////////////////////////////////////
|
|
{
|
|
public :
|
|
template<typename TAutoKey> friend class ZtCAutoKeyRev;
|
|
private:
|
|
ZtCAutoKey(const ZtCAutoKey&){}
|
|
private:
|
|
|
|
TCriticSectEasy& mr_SyncEasy;
|
|
|
|
#ifdef _DEBUG_CAUTOKEY_
|
|
static int msi_CallCnt;
|
|
#endif //_DEBUG_CAUTOKEY_
|
|
|
|
public:
|
|
|
|
ZtCAutoKey(TCriticSectEasy& AR_SyncEasy):mr_SyncEasy(AR_SyncEasy)
|
|
{
|
|
#ifdef _DEBUG_CAUTOKEY_
|
|
cout<<"▶▶▶▶ ZtCAutoKey:: ZtCAutoKey() "<<this<<", "<<++msi_CallCnt<<" ▶▶▶▶"<<endl;
|
|
#endif //_DEBUG_CAUTOKEY_
|
|
|
|
mr_SyncEasy.Lock();
|
|
}/*
|
|
ZtCAutoKey(TCriticSectEasy& AR_SyncEasy)*/
|
|
|
|
~ZtCAutoKey()
|
|
{
|
|
#ifdef _DEBUG_CAUTOKEY_
|
|
cout<<"◀◀◀◀ ZtCAutoKey::~ZtCAutoKey() "<<this<<", "<<--msi_CallCnt<<" ◀◀◀◀"<<endl;
|
|
#endif //_DEBUG_CAUTOKEY_
|
|
|
|
mr_SyncEasy.UnLock();
|
|
}/*
|
|
ZtCAutoKey()*/
|
|
|
|
public:
|
|
};/*
|
|
template<typename TCriticSectEasy=ZCThreadMutexEasy> class ZtCAutoKey */
|
|
|
|
#ifdef _DEBUG_CAUTOKEY_
|
|
|
|
template<typename TCriticSectEasy>
|
|
int ZtCAutoKey<TCriticSectEasy>::msi_CallCnt = 0 ;
|
|
|
|
#endif //_DEBUG_CAUTOKEY_
|
|
|
|
|
|
/*////////////////////////////////////////////////////////////////
|
|
|
|
■ ZtCAutoKeyRev<> 는 ZtCAutoKey<> 을 생성자에서 인수로 받아서 생
|
|
성자에서 Lock 을 해제하고, 소멸자에서 다시 Lock 을 걸어 준다.
|
|
그런데 이렇게 해야 될 상황 자체를 만들지 말자. 억지로 사용하다
|
|
보면 자주 Dead Lock 상태에 빠진다.
|
|
|
|
※ 2008-04-09 21:01:00
|
|
|
|
////////////////////////////////////////////////////////////////*/
|
|
|
|
template<typename TAutoKey> class ZtCAutoKeyRev
|
|
{
|
|
private:
|
|
TAutoKey& mr_CAutoKey;
|
|
public :
|
|
|
|
ZtCAutoKeyRev(TAutoKey& AR_CAutoKey):mr_CAutoKey(AR_CAutoKey)
|
|
{
|
|
mr_CAutoKey.mr_SyncEasy.UnLock();
|
|
}/*
|
|
ZtCAutoKeyRev(TAutoKey& AR_CAutoKey)*/
|
|
|
|
~ZtCAutoKeyRev()
|
|
{
|
|
mr_CAutoKey.mr_SyncEasy.Lock();
|
|
}/*
|
|
~ZtCAutoKeyRev()*/
|
|
|
|
public:
|
|
};/*
|
|
template<typename TAutoKey> class ZtCAutoKeyRev */
|
|
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
class ZCSpinLockProcess
|
|
{
|
|
public :
|
|
enum{EReturn_ShareMemNone = -2}; // 해당 공유메모리 없음
|
|
enum{EReturn_ShareMemMapErr= -3}; // 공유메모리 Map 실패
|
|
private:
|
|
|
|
pthread_spinlock_t* mph_SpinLock ;
|
|
ZCShareMemory mo_CShareMemory;
|
|
|
|
public :
|
|
|
|
int Init(long AL_MemMapkey)
|
|
{
|
|
if(mo_CShareMemory.ShmGet(AL_MemMapkey, sizeof(pthread_spinlock_t))==false)
|
|
{
|
|
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MemMapkey)!=true)
|
|
{
|
|
return this->EReturn_ShareMemNone;
|
|
}/*
|
|
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MemMapkey)!=true)*/
|
|
|
|
if(this->mo_CShareMemory.LinkMap()==false)
|
|
{
|
|
this->mo_CShareMemory.Close(); return this->EReturn_ShareMemMapErr;
|
|
}/*
|
|
if(this->mo_CShareMemory.LinkMap()==false)*/
|
|
|
|
mph_SpinLock =
|
|
(pthread_spinlock_t*)mo_CShareMemory.GetStartAddress();
|
|
|
|
return 0;
|
|
}
|
|
else // mo_CShareMemory.ShmGet(AL_MemMapkey, sizeof(pthread_spinlock_t))!=false
|
|
{
|
|
if(this->mo_CShareMemory.LinkMap()==false)
|
|
{
|
|
this->mo_CShareMemory.Close(); return this->EReturn_ShareMemMapErr;
|
|
}/*
|
|
if(this->mo_CShareMemory.LinkMap()==false)*/
|
|
|
|
mph_SpinLock=(pthread_spinlock_t*)mo_CShareMemory.GetStartAddress();
|
|
|
|
::memset((void*)mph_SpinLock, 0x00, sizeof(pthread_spinlock_t));
|
|
|
|
return ::pthread_spin_init(mph_SpinLock, PTHREAD_PROCESS_SHARED);
|
|
}/*
|
|
else // mo_CShareMemory.ShmGet(AL_MemMapkey,sizeof(pthread_spinlock_t))!=false*/
|
|
}/*
|
|
int Init(long AL_MemMapkey)*/
|
|
|
|
int Close()
|
|
{
|
|
mo_CShareMemory.UnMap();
|
|
mo_CShareMemory.Close();
|
|
|
|
return ::pthread_spin_destroy(mph_SpinLock);
|
|
}/*
|
|
void Close()*/
|
|
|
|
int Lock()
|
|
{
|
|
return ::pthread_spin_lock(mph_SpinLock);
|
|
}/*
|
|
void Lock()*/
|
|
|
|
int TryLock()
|
|
{
|
|
return ::pthread_spin_trylock(mph_SpinLock);
|
|
}/*
|
|
void TryLock()*/
|
|
|
|
int UnLock()
|
|
{
|
|
return ::pthread_spin_unlock(mph_SpinLock);
|
|
}/*
|
|
void UnLock()*/
|
|
|
|
public:
|
|
};/*
|
|
class ZCSpinLockProcess*/
|
|
|
|
|
|
typedef ZCSpinLock ZCCriticSect;
|
|
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ http://blog.naver.com/kimsk99?Redirect=Log&logNo=50004383787
|
|
|
|
윈도우 프로그램을 하면서 쓰레드를 생성할 때, CreateThread와 _beginthread라는 두 함수
|
|
중 어떤 함수를 사용해야 할 지 고민하게 된다. 난 항상 뭔가 기본 API같은 CreateThread
|
|
를 사용해 왔었다. 그런데 오늘 책을 읽다가 그게 잘못되었다는 것을 알았다. MSDN 에 나
|
|
온 설명을 자세히 읽어보면 나오는 내용이었지만 보통 자세히는 안 읽으니 그동안 몰랐
|
|
었다. 일단 CreateThread 는 윈도우 SDK에서 제공하는 기본 API이다. 특별한 라이브러리와
|
|
링크하지 않아도 윈도우라면 컴파일되고 돌아간다. 하지만
|
|
|
|
_beginthread(또는 _beginthreadex)
|
|
|
|
는 standard C library에 속한 함수로 링크해야 동작한다. 그렇기 때문에 기능에서도 차이
|
|
가 난다. 사실 기능에서 차이가 나지 않는다면 이렇게 구별할 이유가 없었을 것이다.
|
|
|
|
_beginthread 와 같은 경우 인자의 종류가 달라서 쓰레드 생성시 필요한 설정을 할 수 없
|
|
는 경우가 있다. 그러나 비슷한 함수인 _beginthreadex 는 완전히 CreateThread와 같은
|
|
인자를 가지고 있다. 그렇기 때문에 CreateThread가 쓰인 모든 곳은 _beginthreadex로 치
|
|
환이 가능하다.
|
|
|
|
내부동작에서 가장 큰 차이점은 _beginthread 는 standard C library에서 사용하는 영역에
|
|
대한 TLB 초기화를 해준다는 것이다. standary C library에 있는 함수 중 일부는 thread-
|
|
safety를 위해서 TLB 를 사용하는데 초기화가 되지 않는다면 문제가 발생할 수 있다.
|
|
|
|
_beginthreadex 함수를 사용해서 쓰레드를 구동했다면 스레드 함수 내에서 쓰레드를 종료
|
|
하기 위해서 ExitThread 함수를 사용하기 보다는 _endthreadex 함수를 사용하길 권장한다.
|
|
|
|
대부분의 경우 C library를 사용하기 때문에 쓰레드의 생성은 _beginthreadex 를 사용해야
|
|
한다.
|
|
|
|
uintptr_t _beginthreadex
|
|
(
|
|
void *security,
|
|
unsigned stack_size,
|
|
unsigned ( *start_address )( void * ),
|
|
void *arglist,
|
|
unsigned initflag,
|
|
unsigned *thrdaddr
|
|
);
|
|
|
|
HANDLE CreateThread
|
|
(
|
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
|
SIZE_T dwStackSize,
|
|
LPTHREAD_START_ROUTINE AP_StartAddress,
|
|
LPVOID lpParameter,
|
|
DWORD dwCreationFlags,
|
|
LPDWORD lpThreadId
|
|
);
|
|
|
|
CreateThread 는 _beginthreadex 로 대체될 수 있고 그래야 한다.
|
|
|
|
dwCreationFlags
|
|
[in] Flags that control the creation of the thread.
|
|
If the CREATE_SUSPENDED flag is specified,
|
|
the thread is created in a suspended state,
|
|
and will not run until the ResumeThread function is called.
|
|
If this value is zero,
|
|
the thread runs immediately after creation.
|
|
If the STACK_SIZE_PARAM_IS_A_RESERVATION flag is specified,
|
|
the dwStackSize parameter specifies the initial reserve size of the stack.
|
|
Otherwise, dwStackSize specifies the commit size.
|
|
|
|
Windows 2000/NT and Windows Me/98/95:
|
|
The STACK_SIZE_PARAM_IS_A_RESERVATION flag is not supported.
|
|
lpThreadId
|
|
[out] Pointer to a variable that receives the thread identifier.
|
|
If this parameter is NULL, the thread identifier is not returned.
|
|
|
|
Windows Me/98/95: This parameter may not be NULL.
|
|
|
|
cf) DWORD ResumeThread( HANDLE hThread );
|
|
|
|
|
|
■ IN WINBASE.H
|
|
|
|
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)
|
|
(
|
|
LPVOID lpThreadParameter
|
|
);
|
|
|
|
typedef struct _SECURITY_ATTRIBUTES
|
|
{
|
|
DWORD nLength;
|
|
LPVOID lpSecurityDescriptor;
|
|
BOOL bInheritHandle;
|
|
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
|
|
|
|
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
|
|
|
|
LPTHREAD_START_ROUTINE AP_StartAddress 는 시작함수포인터
|
|
원형은 DWORD WINAPI ThreadFunc(LPVOID lpRarameter)
|
|
|
|
■ MFC 의 동기화 object
|
|
|
|
아래 object 를 쓰기 위해서는 <afxmt.h> 가 필요
|
|
|
|
1) CCriticalSection cs;
|
|
cs.Lock();
|
|
// 작업코드
|
|
cs.Unlock();
|
|
|
|
2) CEvent event;
|
|
Thread 들이 참조할 수 있도록 전역 또는 상위 블럭에
|
|
CEvent object 를 선언하고 Thread CallBack 함수에서
|
|
|
|
UINT ThreadFunc(LPVOID pParam)
|
|
{
|
|
while(TRUE)
|
|
{
|
|
CEventOvj.Lock(); // 잠이든다.
|
|
|
|
// 이벤트가 발생할 때 실행될 코드를 적는다.
|
|
// 그리고 외부에서
|
|
// CEventOvj.PulseEvent(); 이나
|
|
// CEventOvj.SetEvent(); 를 호출하면 깨어난다.
|
|
|
|
// Thread 가 Lock 이 걸려 있지 않은 상태에서
|
|
// PulseEvent 함수를 먼저 호출하면 아무일도 일어나지 않고
|
|
// 바로 다음 순간에 Lock() 를 호출하면 다시 잠을 잔다.
|
|
// SetEvent() 의 경우에는 그냥 통과한다.
|
|
}
|
|
//while(TRUE)
|
|
}
|
|
//UINT ThreadFunc(LPVOID pParam)
|
|
|
|
|
|
# Window Thread Callback Type;
|
|
|
|
DWORD WINAPI ThreadFunc(LPVOID lpParameter);
|
|
|
|
cf) typedef unsigned long DWORD;
|
|
|
|
# Linux Thread Callback Type:
|
|
|
|
void* ThreadFunc()(void*)
|
|
|
|
■ -- MFC 의 동기화 object
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
typedef pthread_t ThreadID;
|
|
|
|
|
|
#define _THREAD_RETURN_ void*
|
|
|
|
|
|
namespace ZNsIFace
|
|
{
|
|
class ZCThread_BASE
|
|
{
|
|
};/*
|
|
class ZCThread_BASE*/
|
|
}/*
|
|
namespace ZNsIFace*/
|
|
|
|
|
|
template< typename TTypeBase=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCThreadAttr : public TTypeBase ////////
|
|
{
|
|
protected:
|
|
pthread_attr_t mh_ThreadAttr;
|
|
public :
|
|
|
|
ZtCThreadAttr()
|
|
{
|
|
}/*
|
|
ZtCThreadAttr()*/
|
|
|
|
ZtCThreadAttr(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)
|
|
{
|
|
}/*
|
|
ZtCThreadAttr(TTypeBase& AR_CBaseType)*/
|
|
|
|
template<typename TTypeArg>
|
|
ZtCThreadAttr(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg)
|
|
{
|
|
}/*
|
|
template<typename TTypeArg>
|
|
ZtCThreadAttr(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg) */
|
|
|
|
pthread_attr_t* GetThreadAttrPtr()
|
|
{
|
|
return &mh_ThreadAttr ;
|
|
}/*
|
|
pthread_attr_t* GetThreadAttrPtr()*/
|
|
|
|
int Init()
|
|
{
|
|
/*////////////////////////////////////////
|
|
|
|
RETURN VALUES
|
|
On success, pthread_attr_init() returns 0. On error,
|
|
one of the following values is returned:
|
|
|
|
ENOMEM
|
|
Insufficient memory exists to create the thread attribute object.
|
|
|
|
EINVAL
|
|
attr does not point to writable memory.
|
|
|
|
EFAULT
|
|
attr is an invalid pointer
|
|
|
|
////////////////////////////////////////*/
|
|
|
|
return pthread_attr_init(&mh_ThreadAttr);
|
|
}/*
|
|
int Init()*/
|
|
|
|
int Fini()
|
|
{
|
|
return pthread_attr_destroy(&mh_ThreadAttr);
|
|
}/*
|
|
int Fini()*/
|
|
|
|
int GetDetachState(int& ARRI_DetachState)
|
|
{
|
|
return pthread_attr_getdetachstate(&mh_ThreadAttr, &ARRI_DetachState);
|
|
}/*
|
|
int GetDetachState(int ARRI_DetachState)*/
|
|
|
|
int SetDetachState(int AI_DetachState=PTHREAD_CREATE_DETACHED) // cf) PTHREAD_CREATE_JOINABLE
|
|
{
|
|
return pthread_attr_setdetachstate(&mh_ThreadAttr, AI_DetachState);
|
|
}/*
|
|
int SetDetachState(int AI_DetachState=PTHREAD_CREATE_DETACHED)*/
|
|
|
|
int GetSchedPolicy(int& ARRI_Policy)
|
|
{
|
|
return pthread_attr_getschedpolicy(&mh_ThreadAttr, &ARRI_Policy);
|
|
}/*
|
|
int GetSchedPolicy(int ARRI_Policy)*/
|
|
|
|
/*////////////////////////////////////////////////////////////
|
|
|
|
■ pthread_attr_setschedpolicy() 의 2 번 인수에 사용가능한 값
|
|
|
|
SCHED_FIFO
|
|
First in-first out (FIFO) scheduling policy.
|
|
|
|
SCHED_RR
|
|
Round robin scheduling policy.
|
|
|
|
SCHED_OTHER
|
|
Another scheduling policy
|
|
|
|
////////////////////////////////////////////////////////////*/
|
|
|
|
int SetSchedPolicy(int AI_Policy)
|
|
{
|
|
return pthread_attr_setschedpolicy(&mh_ThreadAttr, AI_Policy);
|
|
}/*
|
|
int SetSchedPolicy(int AI_Policy)*/
|
|
|
|
int GetSchedParam(const struct sched_param& ARR_SchedParam)
|
|
{
|
|
// struct sched_param{int sched_priority;};
|
|
|
|
return pthread_attr_getschedparam(&mh_ThreadAttr, &ARR_SchedParam);
|
|
}/*
|
|
int GetSchedParam(const struct sched_param& ARR_SchedParam)*/
|
|
|
|
int SetSchedParam(const struct sched_param& AR_SchedParam)
|
|
{
|
|
return pthread_attr_setschedparam(&mh_ThreadAttr, &AR_SchedParam);
|
|
}/*
|
|
int SetSchedParam(const struct sched_param& AR_SchedParam)*/
|
|
|
|
int GetStackSize(size_t& ARRI_StackSize)
|
|
{
|
|
return pthread_attr_getstacksize(&mh_ThreadAttr, &ARRI_StackSize);
|
|
}/*
|
|
int GetStackSize(size_t& ARRI_StackSize)*/
|
|
|
|
int SetStackSize(size_t AI_StackSize)
|
|
{
|
|
// PTHREAD_STACK_MIN 이상이어야 한다.
|
|
|
|
return pthread_attr_setstacksize(&mh_ThreadAttr, AI_StackSize);
|
|
}/*
|
|
int SetStackSize(size_t AI_StackSize)*/
|
|
|
|
/*/////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 데이비드 R. 부트노프 저 'POSIX 쓰레드를 이용한 프로그래밍' 200 page 에
|
|
|
|
시스템에서 _POSIX_THREAD_ATTR_STACKSIZE 기호 상수를 정의하고 있다면,
|
|
size_t statcksize 속성을 사용할 수 있다.
|
|
|
|
고 되어 있는데, CentOS 64 bit 에서
|
|
|
|
find /usr/include/ | xargs grep 'POSIX_THREAD_ATTR_STACKSIZE'
|
|
|
|
를 실행하면,
|
|
|
|
/usr/include/bits/posix_opt.h:#define _POSIX_THREAD_ATTR_STACKSIZE 200112L
|
|
|
|
을 찾을 수 있다. -- 2011-05-23 20:44:00
|
|
|
|
■ 역시 같은 책, 같은 페이지에
|
|
|
|
POSIX 쓰레드에서는 한 쓰레드에서 요구하는 스택의 최소 크기를 나타내는
|
|
기호 상수로서 PTHREAD_STACK_MIN 을 정의한다.
|
|
|
|
이 기호를 CentOS 에서 찾으면 bits/local_lim.h 에
|
|
|
|
#define PTHREAD_STACK_MIN 16384
|
|
|
|
라고 되어 있다. -- 2011-05-23 20:55:00
|
|
|
|
/////////////////////////////////////////////////////////////////////////*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TTypeBase=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCThreadAttr /////////////////////////*/
|
|
|
|
|
|
template< typename TTypeBase=ZNsIFace::ZCThread_BASE
|
|
>
|
|
class ZtCThread : public TTypeBase ///////////////////
|
|
{
|
|
protected:
|
|
ThreadID mh_ThreadID;
|
|
public :
|
|
|
|
ZtCThread()
|
|
{
|
|
_DEBUG_REENTRANT_CHECK_
|
|
|
|
mh_ThreadID = 0 ;
|
|
}
|
|
ZtCThread(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)
|
|
{
|
|
_DEBUG_REENTRANT_CHECK_
|
|
|
|
mh_ThreadID = 0 ;
|
|
}
|
|
ZtCThread(const ZtCThread& rhs):TTypeBase(rhs)
|
|
{
|
|
_DEBUG_REENTRANT_CHECK_
|
|
|
|
mh_ThreadID = 0 ;
|
|
}/*
|
|
ZtCThread(const ZtCThread& rhs):TTypeBase(rhs)*/
|
|
|
|
template<typename TTypeArg>
|
|
ZtCThread(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg)
|
|
{
|
|
_DEBUG_REENTRANT_CHECK_
|
|
|
|
mh_ThreadID = 0 ;
|
|
}/*
|
|
template<typename TTypeArg>
|
|
ZtCThread(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg)
|
|
*/
|
|
ZtCThread& operator=(const ZtCThread& rhs)
|
|
{
|
|
*(TTypeBase*)(this) = rhs; return *this;
|
|
}/*
|
|
ZtCThread& operator=(const ZtCThread& rhs)*/
|
|
|
|
ZtCThread& operator=(const TTypeBase& AR_CBaseType)
|
|
{
|
|
*(TTypeBase*)(this) = AR_CBaseType; return *this;
|
|
}/*
|
|
ZtCThread& operator=(const TTypeBase& AR_CBaseType)*/
|
|
|
|
|
|
operator ThreadID () const
|
|
{
|
|
return mh_ThreadID;
|
|
}/*
|
|
operator ThreadID () const*/
|
|
|
|
ThreadID GetThreadID() const
|
|
{
|
|
return mh_ThreadID;
|
|
}/*
|
|
ThreadID GetThreadID() const*/
|
|
|
|
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)
|
|
{
|
|
return ::pthread_create( ///////////////
|
|
&mh_ThreadID ,
|
|
AP_ThreadAttr ,
|
|
AP_StartAddress ,
|
|
AP_Arg
|
|
/*/////////*/ ) == 0 ; /////////////////
|
|
}/*
|
|
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)*/
|
|
|
|
|
|
// 쓰레드 종료시까지 메인 함수의 실행을 지연(블럭)시킨다.
|
|
|
|
bool Wait()
|
|
{
|
|
return ::pthread_join(mh_ThreadID, NULL)==0 ;
|
|
}/*
|
|
bool Wait()*/
|
|
|
|
bool Wait(void** APP_ThreadReturn)
|
|
{
|
|
return ::pthread_join(mh_ThreadID, APP_ThreadReturn)==0 ;
|
|
}/*
|
|
bool Wait(void** APP_ThreadReturn)*/
|
|
|
|
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)
|
|
{
|
|
return ::pthread_join(AI_ThreadIDVar, APP_ThreadReturn)==0 ;
|
|
}/*
|
|
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)*/
|
|
|
|
|
|
/*////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ pthread_detach 는 프로세스에서 fork() 후에 waitpid() 를 하지 않아도 되도록
|
|
SIGCHILD 를 무시하는 것과 동일하다.
|
|
|
|
그리고 병합(::pthread_join) 하지 않을 거면, 반드시 호출해야 하는 것으로 기억하자.
|
|
pthread_detach() 을 호출하지 않으면 쓰레드가 종료했는데도 메모리가 OS 에 반환되지
|
|
않을 수 있다. www.wordncode.com 의 파일서버를 만들어 줄 때 이 코드를 빼먹었더니만
|
|
클라이언트가 종료했는데도 계속 메모리가 줄어들지 않고 증가만 하는 것이었다. 그래서
|
|
어느 시점에 (가상)메모리를 소진하여 클라이언트 접속시에 컨넥션 쓰레드를 만들지 못
|
|
하고 있었다!
|
|
|
|
-- 2009-02-24 03:08:00
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
static bool Detach(pthread_t AI_ThreadID)
|
|
{
|
|
return ::pthread_detach(AI_ThreadID)==0;
|
|
}/*
|
|
static bool Detach(pthread_t AI_ThreadID)*/
|
|
|
|
bool Detach()
|
|
{
|
|
return ::pthread_detach(mh_ThreadID)==0;
|
|
}/*
|
|
bool Detach()*/
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ 쓰레드를 취소(pthread_cancel)한다는 것은 다른 쓰레드로부터 취소요청을 받아서
|
|
작동을 중지하는 것을 의미합니다. 마치 kill 을 이용해서 프로세스를 중단시키는
|
|
것과 비슷합니다. 그러나 중단을 요청받은 쓰레드가 바로 중단되는가? 그것은 아닙
|
|
니다. kill 로 프로세스를 죽이는 것도 실제로는 프로세스를 죽이는 시그널을 보내
|
|
고 그 다음은 프로세스가 시그널을 처리하여 죽어줘야 하는데 가끔 죽지 않을 수도
|
|
있거나 혹은 지연(deferred) 되어 죽는 경우도 발생합니다. 쓰레드의 취소도 이와
|
|
같이 바로 죽어주지는 않습니다. 물론 즉시 취소되도록 설정할 수도 있읍니다만,
|
|
쓰레드 취소의 기본 세팅은 지연된(deferred) 취소를 의미합니다.
|
|
|
|
리눅스 시스템 네트워크 프로그래밍 : 가메출판사 : 김선영 저 315 page
|
|
|
|
■ cf) int pthread_setcancelstate(int state,int* oldstate) : 원자적 함수
|
|
|
|
state 은 PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE 중의 한 값
|
|
|
|
//////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
static bool Cancel(pthread_t AI_ThreadID)
|
|
{
|
|
return ::pthread_cancel(AI_ThreadID)==0;
|
|
}/*
|
|
static bool Cancel(pthread_t AI_ThreadID)*/
|
|
|
|
bool Cancel()
|
|
{
|
|
return ::pthread_cancel(mh_ThreadID)==0;
|
|
}/*
|
|
bool Cancel()*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TTypeBase=ZNsIFace::ZCThread_BASE
|
|
>
|
|
class ZtCThread ////////////////////////////////////*/
|
|
|
|
|
|
template<> class ZtCThread<ZNsIFace::ZCThread_BASE>
|
|
{
|
|
protected:
|
|
ThreadID mh_ThreadID;
|
|
public :
|
|
|
|
ZtCThread()
|
|
{
|
|
_DEBUG_REENTRANT_CHECK_
|
|
}/*
|
|
ZtCThread()*/
|
|
|
|
ZtCThread(const ZtCThread& rhs)
|
|
{
|
|
mh_ThreadID=rhs.mh_ThreadID;
|
|
}/*
|
|
ZtCThread(const ZtCThread& rhs)*/
|
|
|
|
ZtCThread& operator=(const ZtCThread& rhs)
|
|
{
|
|
return *this; // nothing to do
|
|
}/*
|
|
ZtCThread& operator=(const ZtCThread& rhs)*/
|
|
|
|
operator ThreadID () const
|
|
{
|
|
return mh_ThreadID;
|
|
}/*
|
|
operator ThreadID () const*/
|
|
|
|
ThreadID GetThreadID() const
|
|
{
|
|
return mh_ThreadID;
|
|
}/*
|
|
ThreadID GetThreadID() const*/
|
|
|
|
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)
|
|
{
|
|
return ::pthread_create( //////////////
|
|
&mh_ThreadID ,
|
|
AP_ThreadAttr ,
|
|
AP_StartAddress ,
|
|
AP_Arg
|
|
/*/////////*/ ) == 0 ; ////////////////
|
|
}/*
|
|
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)*/
|
|
|
|
bool Wait()
|
|
{
|
|
return ::pthread_join(mh_ThreadID, NULL)==0 ;
|
|
}/*
|
|
bool Wait()*/
|
|
|
|
bool Wait(void** APP_ThreadReturn)
|
|
{
|
|
return ::pthread_join(mh_ThreadID, APP_ThreadReturn)==0 ;
|
|
}/*
|
|
bool Wait(void** APP_ThreadReturn)*/
|
|
|
|
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)
|
|
{
|
|
return ::pthread_join(AI_ThreadIDVar, APP_ThreadReturn)==0 ;
|
|
}/*
|
|
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)*/
|
|
|
|
static bool Detach(pthread_t AI_ThreadID)
|
|
{
|
|
return ::pthread_detach(AI_ThreadID)==0;
|
|
}/*
|
|
static bool Detach(pthread_t AI_ThreadID)*/
|
|
|
|
bool Detach()
|
|
{
|
|
return ::pthread_detach(mh_ThreadID)==0;
|
|
}/*
|
|
bool Detach()*/
|
|
|
|
static bool Cancel(pthread_t AI_ThreadID)
|
|
{
|
|
return ::pthread_cancel(AI_ThreadID)==0;
|
|
}/*
|
|
static bool Cancel(pthread_t AI_ThreadID)*/
|
|
|
|
bool Cancel()
|
|
{
|
|
return ::pthread_cancel(mh_ThreadID)==0;
|
|
}/*
|
|
bool Cancel()*/
|
|
|
|
public:
|
|
};/*
|
|
template<> class ZtCThread<ZNsIFace::ZCThread_BASE>*/
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ class ZtCMutexCond 는 window 에는 없는 리눅스 뮤텍스 조건 변수 클래스
|
|
|
|
이것이 유용한 경우는 다수의 쓰레드가 함수 A, 함수 B, 함수 C 를 수행하는데, 반드시 함
|
|
수 A -> B -> C 이 순서로 실행해야 하고 각 함수는 동기화를 해주어야 할 때, 쓰레드 조
|
|
건변수를 사용하면 한 개의 뮤텍스로 가능하나 (조건변수는 3-1 개), 뮤텍스로만 한다면 A,
|
|
B,C 용 각각 3 개의 뮤텍스를 사용해야 한다. 즉 한 개의 뮤텍스에 여러 개의 조건 변수가
|
|
대응할 수 있다. 반대로 한 개의 조건변수에 여러개의 뮤텍스가 대응할 수는 없다.
|
|
|
|
조건 변수는 복사 사용해서는 안된다. 따라서 복사 생성자와 대입 연산자의 실행을 막는다.
|
|
|
|
조건 변수는 반드시 한번만 초기화 되어야 한다.
|
|
|
|
■ 어떤 작업 큐에 다수의 쓰레드가 접근하여 작업을 가져온다고 가정하자. 작업큐에 접근하는
|
|
쓰레드는, 큐에 접근하는 동안은 다른 쓰레드가 동시 접근하지 않도록 '작업 큐 접근 동기
|
|
화 object ' 에 lock 을 걸고 해당 큐가 비어있는지 그렇지 않은지를 조사할 것이다. 이때
|
|
큐에 등록된 작업이 하나라도 있다면 그 작업을 가져오고 '작업큐 접근 동기화 object' 를
|
|
unlock 할 것이다. 만일 큐에 등록된 작업이 없다면 '작업 큐 접근 동기화 object' 를 unlock
|
|
하고 자신은 작업큐에 작업이 등록될 때까지 블럭되어야 한다. 이때 자신이 블럭되고 있다
|
|
는 것을 알리는 표시를 하고(그래야 나중에 '깨울' 수 있으니까) 블럭되야 하는데, 이 사이
|
|
에 즉 unlock 과 블럭표시를 하고 블럭되는 사이에 다른 쓰레드가 끼어들어 '작업큐 접근
|
|
동기화 object' 에 lock 을 걸고 작업을 등록하면, 작업은 분명 있는데 블럭표시를 하려는
|
|
쓰레드는 블럭표시를 하기도 전이므로, 다른 쓰레드는 그 쓰레드가 블럭되고 있지 않다고
|
|
보고 별 처리없이 '작업큐 접근 동기화 object' 에 unlock 을 하고 다음을 진행할 것이다.
|
|
그러면 블럭표시를하고 블럭되려는 쓰레드는 블럭이 해제될 기회를 갖지 못한 채 영원히 블
|
|
럭될 수 있다. 이 사태를 방자하기 위해 '작업큐 접근 동기화 object' 에 unlock 을 하는
|
|
작업과 블럭상태로 진입하는 명령은 원자적으로 이루어져야 한다. 이러면 다른 쓰레드가 블
|
|
럭되고 있는 쓰레드를 정확하게 판별할 수 있게끔 보장해준다. 이것이 조건 변수가 존재하
|
|
는 이유이다.
|
|
|
|
조건 변수 대기는 항상 뮤텍스를 잠근 채로 리턴해야 한다.
|
|
(물론 리턴하기 전, 대기하고 있을 동안은 뮤텍스를 잠그지 않은 상태다.)
|
|
|
|
■ 뮤텍스를 잠그지 않은 채로 얼마든지 조건변수로 시그널을 보내거나 브로드캐스트 할 수 있
|
|
다. 어떤 쓰레드가 조건변수로 시그널을 보내거나 브로드캐스트한다 할지라도 대상 조건 변
|
|
수에서 대기 중인 쓰레드가 하나도 없다면 아무일도 일어나지 않는다. 잠시 후 다른 쓰레드
|
|
가 pthread_cond_wait() 함수를 호출한다면, 바로 전에 해당 조건 변수로 시그널이 보내졌
|
|
다는 것을 알지 못하고 그저 기다리는 수밖에 없다. 따라서 이 시점이 절대로 기다려서는
|
|
안되는 상황이라면 쓰레드는 결코 깨어날 수 없게 된다. 쓰레드가 조건변수에서 블럭될 때
|
|
까지 뮤텍스는 잠긴 채로 남아 있기 때문에, 술어 검사와 대기도중에는(다른 쓰레드들에 의
|
|
해) 술어가 설정될 수 없다. 즉 뮤텍스가 잠겨 있으므로 다른 쓰레드들이 술어 등의 공유
|
|
데이타들을 변경할 수 없는 것이다.
|
|
|
|
■ 항상 술어를 테스트하라! 그리고 다시 한번 테스트하라!
|
|
|
|
쓰레드가 깨어났을 때, 다시 술어를 테스트하는 것 역시 매우 중요하다. 따라서 조건 변수
|
|
대기는 항상 루프를 이용하여 수행하는 것이 좋다.
|
|
|
|
-- 데이비드 R 부트노프 저 Posix 쓰레드를 이용한 프로그래밍 123P
|
|
|
|
각 조건변수는 특정 뮤텍스 및 술어 조건과 결합되어야 한다. 또한 쓰레드가 조건 변수상
|
|
에서 대기할 때는 항상 결합된 뮤텍스를 잠그고 있어야 한다. 조건변수 대기명령을 사용하
|
|
면, 쓰레드를 블록하기 전에 뮤텍스의 잠금을 해제하고 원래 코드로 리턴하기 전에 뮤텍스
|
|
를 다시 잠근다는 사실을 기억하자
|
|
|
|
-- 데이비드 R 부트노프 저 Posix 쓰레드를 이용한 프로그래밍 123P
|
|
|
|
■ window 에서는 뮤텍스 조건 변수를 구현하기가 대단히 힘든데, 만일 조건 변수의 기능을
|
|
특정 쓰레드의 "대기"와 "깨어남" 으로 한정한다면 IOCP 와 GetQueuedCompletionStatus()
|
|
함수를 이용하여 구현할 수 있다.
|
|
|
|
window 에서 조건변수는 이벤트로 구현하는 것이 제일 무난한 것 같다.
|
|
|
|
-- 2008-12-05 15:06:00
|
|
|
|
■ 이제 생각해보니 조건변수의 중요 역할은 첫째 "대기" 와 둘째 뮤텍스를 잠근 쓰레드가 아
|
|
닌 "다른 쓰레드가 잠금을 해제하는 것" 을 허용하는 것이 아닐까 한다.
|
|
|
|
-- 2007-11-12 07:56:00
|
|
|
|
■ "작업쓰레드" 가 조건변수에서 대기하고 있고, "주쓰레드" 에서 "작업쓰레드" 에 시그널을
|
|
보내 깨우는 과정을 생각해보자. "주쓰레드" 에서 "작업쓰레드" 에 시그널을 보내면, "작업
|
|
쓰레드" 는 뮤텍스에 Lock 을 걸고 대기 함수를 빠져나올려고 할 것이다. 이때 "주쓰레드"
|
|
에서 Lock 을 걸고 시그널을 보냈다면, "작업쓰레드" 는 대기 함수를 리턴하기 전에 이미
|
|
뮤텍스가 잠겨있으므로, 뮤텍스가 풀릴 때까지 다시 대기하게 된다. 따라서 시그널을 보낸
|
|
"주쓰레드" 는 가급적 빨리 Lock 을 풀어주어야 한다. 또한 시그널로 인해 깨어난 "작업쓰
|
|
레드"도 뮤텍스에 Lock 을 걸고 대기 함수를 리턴하게 되면 뮤텍스의 Lock 을 빨리 풀어주
|
|
는 것이 좋다. -- 2007-11-16 05:51:00
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
template< typename TData=ZNsMain::ZCEmpty /////////////
|
|
>
|
|
class ZtCMutexCond : public TData, ZNsMain::ZCNonCopyable
|
|
{
|
|
protected:
|
|
::pthread_cond_t mh_PThread_Cond;
|
|
public :
|
|
|
|
::pthread_cond_t GetHandle() const
|
|
{
|
|
return mh_PThread_Cond;
|
|
}/*
|
|
::pthread_cond_t GetHandle() const*/
|
|
|
|
int InitCond(pthread_condattr_t* AP_CondAttr=NULL)
|
|
{
|
|
return ::pthread_cond_init(&mh_PThread_Cond, AP_CondAttr) ;
|
|
|
|
/*/////////////////////////////////////////////////////////
|
|
|
|
■ pthread_cond_init 의 에러 반환값(성공시 0)
|
|
|
|
EAGIN (메모리이외의) 자원이 부족하다.
|
|
ENOMEM 메모리가 부족하다.
|
|
EBUSY 조건변수가 이미 초기화 되어 있다.
|
|
EINVAL AP_CondAttr 이 올바르지 않다.
|
|
|
|
가급적이면 정적초기화를 사용한다. (PTHREAD_COND_INITIALIZER)
|
|
|
|
/////////////////////////////////////////////////////////*/
|
|
}/*
|
|
int Init(pthread_condattr_t* AP_CondAttr=NULL)*/
|
|
|
|
int FiniCond()
|
|
{
|
|
/*////////////////////////////////////////////////////////////
|
|
|
|
■ pthread_cond_destroy()
|
|
|
|
RETURN VALUES
|
|
On success, pthread_cond_destroy() returns 0.
|
|
On error, one of the following values is returned:
|
|
|
|
EINVAL
|
|
cond does not refer to an initialized condition variable.
|
|
|
|
EBUSY
|
|
The condition variable is in use by another thread (for example, in a condition wait).
|
|
EFAULT
|
|
cond is an invalid pointer
|
|
|
|
■ PTHREAD_COND_INITIALIZER 메크로를 사용하여
|
|
정적으로 초기화된 조건변수는 파괴하지 않아도 된다.
|
|
|
|
////////////////////////////////////////////////////////////*/
|
|
|
|
return ::pthread_cond_destroy(&mh_PThread_Cond) ;
|
|
}/*
|
|
int FiniCond()*/
|
|
|
|
int WaitCond(pthread_mutex_t& AR_MutexArg)
|
|
{
|
|
return ::pthread_cond_wait(&mh_PThread_Cond, &AR_MutexArg) ;
|
|
|
|
/*//////////////////////////////////////////////////////////
|
|
|
|
■ pthread_cond_wait 함수가 제대로 수행되려면 뮤텍스가 초기
|
|
화되어 있고 lock 이 되어 있어야 한다. 이 함수가 실행되면
|
|
함수는 내부에 사용된 뮤텍스의 lock 을 해제한 채로 대기 작
|
|
업에 들어간다. 그리고 조건 변수에 대한 시그널이 검출되면
|
|
작업을 재개한다. 즉 해당조건 변수에 대한 시그널이 발생하
|
|
면 내부의 인수로 지정된 뮤텍스를 잠근후 pthread_cond_wait
|
|
함수의 다음 라인을 실행하게 된다. pthread_cond_timedwait
|
|
함수를 사용하면 timespec 에 지정된 시간만큼 대기를 하게
|
|
된다. 만약 지정된 시간내에 시그널이 검출되지 않으면 타임
|
|
아웃 에러코드와 함께 실행이 중지된다.
|
|
|
|
//////////////////////////////////////////////////////////*/
|
|
}/*
|
|
int WaitCond(pthread_mutex_t& AR_MutexArg)*/
|
|
|
|
int WaitCondTime(pthread_mutex_t& AR_MutexArg, struct timespec& AR_Expriation)
|
|
{
|
|
// time out 이면 ZNsEnum::ZEThread_TimeOut(ETIMEDOUT) 을 반환한다.
|
|
|
|
return ::pthread_cond_timedwait(&mh_PThread_Cond, &AR_MutexArg, &AR_Expriation) ;
|
|
|
|
/*/////////////////////////////////////////////////////
|
|
|
|
DESCRIPTION
|
|
The pthread_cond_timedwait()
|
|
function blocks on the specified condition variable,
|
|
which atomically releases the specified mutex
|
|
and causes the calling thread to block on the condition variable
|
|
The blocked thread may be awakened by a call to pthread_cond_signal()
|
|
or pthread_cond_broadcast(),
|
|
or if the time specified by abstime is reached.
|
|
|
|
This function atomically releases the mutex,
|
|
causing the calling thread to block on the condition variable Upon successful completion,
|
|
the mutex is locked and owned by the calling thread.
|
|
|
|
pthread_cond_timedwait() is the same as pthread_cond_wait(),
|
|
except an error is returned
|
|
if the system time equals or exceeds the time specified by abstime
|
|
before the condition variable is signaled or broadcast,
|
|
or if the absolute time specified by abstime has already passed at the time of the call.
|
|
When timeouts occur, pthread_cond_timedwait() releases and reacquires the mutex.
|
|
|
|
When using condition variables,
|
|
there should always be a boolean predicate involving shared variables related to each condition wait.
|
|
This predicate should become true only
|
|
when the thread should proceed.
|
|
Because the return from pthread_cond_timedwait() does not indicate anything about the value of this predicate,
|
|
the predicate should be reevaluated on return.
|
|
Unwanted wakeups from pthread_cond_timedwait() may occur
|
|
(since another thread could have obtained the mutex,
|
|
changed the state and released the mutex,
|
|
prior to this thread obtaining the mutex);
|
|
the reevaluation of the predicate ensures consistency.
|
|
|
|
The pthread_cond_timedwait() function is a cancellation point.
|
|
If a cancellation request is acted on while in a condition wait
|
|
when the cancellation type of a thread is set to deferred,
|
|
the mutex is reacquired before calling the first cancellation cleanup handler.
|
|
In other words, the thread is unblocked,
|
|
allowed to execute up to the point of returning from the call pthread_cond_timedwait(),
|
|
but instead of returning to the caller, it performs the thread cancellation.
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
PARAMETERS
|
|
|
|
cond
|
|
Is the condition variable to wait on.
|
|
|
|
mutex
|
|
Is the mutex associated with the condition variable.
|
|
|
|
abstime
|
|
Is the absolute time at which the wait is cancelled if not signaled or broadcast.
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
RETURN VALUES
|
|
On success, pthread_cond_timedwait() returns 0.
|
|
On error, one of the following values is returned:
|
|
|
|
EINVAL
|
|
cond does not refer to an initialized condition variable,
|
|
or mutex does not refer to an initialized mutex.
|
|
Different mutexes were specified in multiple waits on cond.
|
|
mutex is not owned by the caller.
|
|
|
|
EFAULT
|
|
cond, mutex, or abstime is an invalid pointer.
|
|
|
|
ETIMEDOUT
|
|
The specified absolute time has passed.
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
CONFORMANCE
|
|
POSIX P1003.1 (1996)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
MULTITHREAD SAFETY LEVEL
|
|
MT-safe.
|
|
|
|
///////////////////////////////////////////////////////////////////////////////*/
|
|
}/*
|
|
int WaitCondTime(pthread_mutex_t& AR_MutexArg, struct timespec& AR_Expriation)*/
|
|
|
|
int WakeCond()
|
|
{
|
|
return ::pthread_cond_signal(&mh_PThread_Cond);
|
|
}/*
|
|
int WakeCond()*/
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////////////////////
|
|
|
|
■ pthread_cond_broadcast() 함수는 조건 변수에서 대기하는 모든 쓰레드에게 시그널
|
|
을 보낸다. 모든 쓰레드가 일어나게 되므로 대개 경합이 발생할 가능성이 있다. 만
|
|
일 해야 할 작업이 10 개라면 실제로 일을 가져가는 쓰레드는 1 개일 테고 나머지는
|
|
조건을 비교한 뒤에 다시 대기한다. 이렇게 쓸데없이 깨어난 쓰레드들이 다시 대기
|
|
상태로 가는 것은 필연적으로 오버헤드를 발생시키게 된다. 따라서 어떤 경우에 브
|
|
로드캐스트를 쓸 것인지를 잘 생각해서 디자인해야 한다.
|
|
|
|
-- 리눅스 시스템 네트워크 프로그래밍, 김선영 저, 가메출판사
|
|
|
|
///////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
int BoradCast()
|
|
{
|
|
return ::pthread_cond_broadcast(&mh_PThread_Cond) ;
|
|
}/*
|
|
int BoradCast()*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TData=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCMutexCond //////////////////////*/
|
|
|
|
|
|
|
|
/*///////////////////////////////////////////////////////////////
|
|
|
|
■ mutex 와 조건 변수를 결합한 클래스다. ZtCMutexCond<> 템플릿과
|
|
ZCProcessMutex 등을 적절히 이용하면 되지만 좀더 최적화 하기 위
|
|
해서 별도로 구현하는 것이다.
|
|
|
|
///////////////////////////////////////////////////////////////*/
|
|
|
|
template< typename TData=ZNsMain::ZCEmpty /////////////////////
|
|
>
|
|
class ZtCMutexCondData : public TData ///////////////////////////
|
|
{
|
|
protected:
|
|
pthread_mutex_t mh_Mutex;
|
|
pthread_cond_t mh_Cond ;
|
|
public :
|
|
|
|
int InitCond(const pthread_mutexattr_t* AP_MutexAttr=0, const pthread_condattr_t* AP_CondAttr=0)
|
|
{
|
|
#if(_CODE_OLD_)
|
|
|
|
// 실험용 코드.
|
|
|
|
bool VB_IsOK1= (::pthread_mutex_init(&mh_Mutex, AP_MutexAttr)==ZNsMain::ZNsEnum::ZEThread_OK);
|
|
bool VB_IsOK2= (::pthread_cond_init (&mh_Cond, AP_CondAttr )==ZNsMain::ZNsEnum::ZEThread_OK);
|
|
|
|
return (VB_IsOK1 && VB_IsOK2) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
|
|
|
|
#else
|
|
return ( ::pthread_mutex_init(&mh_Mutex, AP_MutexAttr)==ZNsMain::ZNsEnum::ZEThread_OK &&
|
|
::pthread_cond_init (&mh_Cond, AP_CondAttr )==ZNsMain::ZNsEnum::ZEThread_OK
|
|
/*///*/ ) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
|
|
#endif
|
|
}/*
|
|
int Init(const pthread_mutexattr_t* AP_MutexAttr=0, const pthread_condattr_t* AP_CondAttr=0)*/
|
|
|
|
int Lock()
|
|
{
|
|
return ::pthread_mutex_lock(&mh_Mutex);
|
|
}/*
|
|
bool Lock()*/
|
|
|
|
int TryLock(const timespec* AP_TimeOut)
|
|
{
|
|
return ::pthread_mutex_trylock(&mh_Mutex);
|
|
}/*
|
|
int TryLock(const timespec* AP_TimeOut)*/
|
|
|
|
int UnLock()
|
|
{
|
|
return ::pthread_mutex_unlock(&mh_Mutex);
|
|
}/*
|
|
int UnLock()*/
|
|
|
|
int WaitCond() // 해당 뮤텍스에 lock 이 걸려 있어야 한다.
|
|
{
|
|
return ::pthread_cond_wait(&mh_Cond, &mh_Mutex) ;
|
|
}/*
|
|
int WaitCond()*/
|
|
|
|
#ifdef __USE_XOPEN2K
|
|
|
|
int Lock(const timespec* AP_TimeOut)
|
|
{
|
|
return ::pthread_mutex_timedlock(&mh_Mutex, AP_TimeOut);
|
|
}/*
|
|
int Lock(const timespec* AP_TimeOut)*/
|
|
|
|
int WaitCondTime(const timespec* AP_TimeOut)
|
|
{
|
|
return ::pthread_cond_timedwait(&mh_Cond, &mh_Mutex, AP_TimeOut) ;
|
|
}/*
|
|
int WaitCondTime(const timespec* AP_TimeOut)*/
|
|
|
|
int WaitCondTime(int AI_TimeOutMili)
|
|
{
|
|
timespec VO_TimeAbs; // 절대 시간
|
|
timeval VO_TimeNow;
|
|
|
|
/*///////////////////////////////////////////////////////
|
|
|
|
■ timespec
|
|
|
|
struct timespec
|
|
{
|
|
__time_t tv_sec ; // Seconds.
|
|
long int tv_nsec; // Nanoseconds. 10 억분의 1 초, 1 밀리초=1000*1000 나노초
|
|
};
|
|
|
|
■ int gettimeofday( struct timeval *tv, struct timezone *tz);
|
|
int settimeofday(const struct timeval *tv,const struct timezone *tz);
|
|
|
|
-- linux manual 에서
|
|
|
|
struct timeval
|
|
{
|
|
long tv_sec; // 초
|
|
long tv_usec; // 마이크로초
|
|
};
|
|
|
|
struct timezone
|
|
{
|
|
int tz_minuteswest; // 그리니치 서측 분차(minutes)
|
|
int tz_dsttime; // DST 보정 타입
|
|
};
|
|
|
|
timezone struct 는 사용하지 않는다;
|
|
리눅스에서 tz_dsttime 필드가 사용되지 않는다.
|
|
지금까지 그렇고 앞으로도 libc 나 glibc에서 지원되지 않을 것이다.
|
|
커널 소스에서(선언 이외에) 이 필드가 나오는 모든 경우는 버그이다.
|
|
그래서 다음 내용은 순전히 역사적인 흥미거리다.
|
|
|
|
~~~~~~
|
|
|
|
gettimeofday와 settimeofday 모두 성공하면 0을 리턴하며,
|
|
실패시에는 -1을 리턴한다.(errno는 적당한 값으로 설정된다.)
|
|
|
|
///////////////////////////////////////////////////////*/
|
|
|
|
::gettimeofday(&VO_TimeNow, NULL);
|
|
|
|
VO_TimeAbs.tv_sec = VO_TimeNow.tv_sec + (AI_TimeOutMili/1000) ;
|
|
VO_TimeAbs.tv_nsec= VO_TimeNow.tv_usec*1000 + (AI_TimeOutMili%1000)*1000*1000 ;
|
|
|
|
return ::pthread_cond_timedwait(&mh_Cond, &mh_Mutex, &VO_TimeAbs) ; // return ZEThread_TimeOut if timeout
|
|
}/*
|
|
int WaitCondTime(int AI_TimeOutMili)*/
|
|
|
|
#endif //__USE_XOPEN2K
|
|
|
|
int WakeCond()
|
|
{
|
|
return ::pthread_cond_signal(&mh_Cond);
|
|
}/*
|
|
int WakeCond()*/
|
|
|
|
int WakeAllCond()
|
|
{
|
|
return ::pthread_cond_broadcast(&mh_Cond);
|
|
}/*
|
|
int WakeAllCond()*/
|
|
|
|
int BroadCast()
|
|
{
|
|
return WakeAllCond();
|
|
}/*
|
|
int BroadCast()*/
|
|
|
|
int FiniCond()
|
|
{
|
|
#if(_CODE_OLD_)
|
|
|
|
// 실험용 코드.
|
|
|
|
bool VB_IsOK1= (::pthread_cond_destroy (&mh_Cond )==ZNsMain::ZNsEnum::ZEThread_OK);
|
|
bool VB_IsOK2= (::pthread_mutex_destroy(&mh_Mutex)==ZNsMain::ZNsEnum::ZEThread_OK);
|
|
|
|
return (VB_IsOK1 && VB_IsOK2) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
|
|
|
|
#else
|
|
|
|
return ( ::pthread_cond_destroy (&mh_Cond )==ZNsMain::ZNsEnum::ZEThread_OK &&
|
|
::pthread_mutex_destroy(&mh_Mutex)==ZNsMain::ZNsEnum::ZEThread_OK
|
|
/*///*/ ) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
|
|
|
|
#endif
|
|
}/*
|
|
int FiniCond()*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TData=ZNsMain::ZCEmpty /////////////////////
|
|
>
|
|
class ZtCMutexCondData : public TData ///////////////////*/
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////
|
|
|
|
■ ZtCEventCond<> 템플릿은 Window 의 event 와 같이, 특정 조건에서 대
|
|
기하는 모든 쓰레드를 한꺼번에 깨우기 위해서 필요하다.
|
|
|
|
Linux 의 pthread_cond_signal() 함수는 해당 조건 변수에 대기하고
|
|
있는 쓰레드 중 하나를 깨운다. event 효과를 낼려면
|
|
|
|
pthread_cond_broadcast()
|
|
|
|
를 사용해야 한다.
|
|
|
|
단순히 WakeCond() 함수를 WakeAllCond() 함수로 가리고 있다.
|
|
|
|
-- 2009-10-25 23:05:00
|
|
|
|
■ 조건 변수에서 다수의 쓰레드가 대기하고 있는데, signal 을 2 회 연속
|
|
으로 보낸 경우에, 2 개 쓰레드가 연속적으로 깨어난다고 보장할 수 없
|
|
다. signal 1 회 발생시에 context switching 이 일어나서 깨어난 쓰레
|
|
드가 무슨 일을 한 다음에, 다시 조건 변수에서 대기한다면, 2 번째
|
|
signal 에서 그 쓰레드가 또 깨어날 수 있는 것이다.
|
|
|
|
-- 2009-10-26 13:18:00
|
|
|
|
//////////////////////////////////////////////////////////////////*/
|
|
|
|
template< typename TData=ZNsMain::ZCEmpty ///////
|
|
>
|
|
class ZtCEventCond : public ZtCMutexCondData<TData>
|
|
{
|
|
public:
|
|
typedef ZtCMutexCondData<TData> ZCMutexCondData;
|
|
public:
|
|
int WakeCond(){return this->WakeAllCond();}
|
|
public:
|
|
};/*
|
|
template< typename TData=ZNsMain::ZCEmpty ////////
|
|
>
|
|
class ZtCEventCond : public ZtCMutexCondData<TData>*/
|
|
|
|
|
|
typedef ZtCEventCond<> CEventCond;
|
|
|
|
|
|
#if defined(__USE_XOPEN2K) && !defined(__USE_MUTEX_COND_FOR_BARRIER__)
|
|
|
|
template< typename TData=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCBarrier : public TData ///////////
|
|
{
|
|
protected:
|
|
bool mb_IsValidID;
|
|
::pthread_barrier_t mh_BarrierID; // 0 으로 초기화할 수 없다.
|
|
public :
|
|
|
|
ZtCBarrier()
|
|
{
|
|
mb_IsValidID=false;
|
|
}/*
|
|
ZtCBarrier()*/
|
|
|
|
bool IsValid() const{return mb_IsValidID;}
|
|
|
|
|
|
/*/////////////////////////////////////////////////
|
|
|
|
■ Init() 와 Fini() 의 리턴값을 다룰 때는
|
|
|
|
if(CBarrierObj.Init(3)==ZNsEnum::ZEBarrier_OK)
|
|
|
|
이런 식으로 한다.
|
|
|
|
/////////////////////////////////////////////////*/
|
|
|
|
int Init(unsigned AI_Count, const ::pthread_barrierattr_t* AP_Attr=0)
|
|
{
|
|
#if(_CODE_OLD_)
|
|
|
|
if(mb_IsValidID==true)
|
|
{
|
|
::pthread_barrier_destroy(&mh_BarrierID); mb_IsValidID=false;
|
|
}/*
|
|
if(mb_IsValidID==true)*/
|
|
|
|
#endif // _CODE_OLD_
|
|
|
|
int VI_Return = ::pthread_barrier_init(&mh_BarrierID, AP_Attr, AI_Count);
|
|
|
|
if(VI_Return==ZNsEnum::ZEBarrier_OK) mb_IsValidID=true; return VI_Return;
|
|
}/*
|
|
int Init(unsigned AI_Count, const ::pthread_barrierattr_t* AP_Attr=0)*/
|
|
|
|
int Fini()
|
|
{
|
|
if(mb_IsValidID==false) return ZNsEnum::ZEBarrier_OK;
|
|
|
|
mb_IsValidID=false; return ::pthread_barrier_destroy(&mh_BarrierID);
|
|
}/*
|
|
int Fini()*/
|
|
|
|
|
|
/*////////////////////////////////////////////////////////////////////////////
|
|
|
|
int pthread_barrier_wait(pthread_barrier_t *barrier);
|
|
|
|
RETURN VALUE
|
|
Upon successful completion, the pthread_barrier_wait() function shall
|
|
return PTHREAD_BARRIER_SERIAL_THREAD for a single (arbitrary) thread
|
|
synchronized at the barrier and zero for each of the other threads.
|
|
Otherwise, an error number shall be returned to indicate the error.
|
|
|
|
ERRORS
|
|
The pthread_barrier_wait() function may fail if:
|
|
|
|
EINVAL The value specified by barrier does not refer to an initialized
|
|
barrier object.
|
|
|
|
This function shall not return an error code of [EINTR].
|
|
|
|
The following sections are informative.
|
|
|
|
////////////////////////////////////////////////////////////////////////////*/
|
|
|
|
bool Wait()
|
|
{
|
|
return ::pthread_barrier_wait(&mh_BarrierID)!=ZNsEnum::ZEThread_Invalid;
|
|
}/*
|
|
bool Wait()*/
|
|
|
|
|
|
// 아래 2 개의 멤버는 윈도우에는 없다.
|
|
|
|
bool GetShared(::pthread_barrierattr_t AH_BarrierAttr, int& ARRI_PShared)
|
|
{
|
|
return ::pthread_barrierattr_getpshared(&AH_BarrierAttr, &ARRI_PShared)==ZNsEnum::ZEThread_OK;
|
|
}/*
|
|
bool GetShared(::pthread_barrierattr_t AH_BarrierAttr, int& ARRI_PShared)*/
|
|
|
|
/* cf) PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE
|
|
AI_PShared 을 PTHREAD_PROCESS_SHARED 로 지정하는 경우에
|
|
mh_BarrierID 은 공유 메모리에 존재해야 한다. */
|
|
|
|
bool SetShared(::pthread_barrierattr_t AH_BarrierAttr, int AI_PShared=PTHREAD_PROCESS_SHARED)
|
|
{
|
|
return ::pthread_barrierattr_setpshared(&AH_BarrierAttr, AI_PShared)==ZNsEnum::ZEThread_OK;
|
|
}/*
|
|
bool SetShared(::pthread_barrierattr_t AH_BarrierAttr, int AI_PShared=PTHREAD_PROCESS_SHARED)*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TData=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCBarrier : public TData /////////*/
|
|
|
|
|
|
#else // !defined(__USE_XOPEN2K) || defined(__USE_MUTEX_COND_FOR_BARRIER__)
|
|
|
|
|
|
template< typename TData=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCBarrier : public TData ///////////
|
|
{
|
|
public :
|
|
typedef ZtCMutexCondData<> ZCMutexCondData;
|
|
protected:
|
|
ZCMutexCondData mo_CCondData;
|
|
bool mb_IsValidID;
|
|
int mi_WaitCount;
|
|
public :
|
|
|
|
ZtCBarrier():mi_WaitCount(0){mb_IsValidID=false;}
|
|
|
|
bool IsValid() const{return mb_IsValidID;}
|
|
|
|
|
|
/*//////////////////////////////////////////////////
|
|
|
|
■ Init() 와 Fini() 의 리턴값을 다룰 때는
|
|
|
|
if(CBarrierObj.Init(3)==ZNsEnum::ZEBarrier_OK)
|
|
|
|
의 형태로 한다.
|
|
|
|
//////////////////////////////////////////////////*/
|
|
|
|
int Init(unsigned AI_Count)
|
|
{
|
|
if(mb_IsValidID==true)
|
|
{
|
|
mo_CCondData.FiniCond(); mb_IsValidID=false;
|
|
}/*
|
|
if(mb_IsValidID==true)*/
|
|
|
|
mi_WaitCount=AI_Count;
|
|
|
|
int VI_Return = mo_CCondData.InitCond();
|
|
|
|
if(VI_Return==ZNsEnum::ZEThread_OK)
|
|
{
|
|
mb_IsValidID=true; return ZNsEnum::ZEThread_OK;
|
|
}/*
|
|
if(VI_Return==ZNsEnum::ZEThread_OK)*/
|
|
|
|
return ZNsEnum::ZEBarrier_NO;
|
|
}/*
|
|
int Init(unsigned AI_Count)*/
|
|
|
|
int Fini()
|
|
{
|
|
if(mb_IsValidID==false) return ZNsEnum::ZEThread_OK;
|
|
|
|
mb_IsValidID=false;
|
|
mi_WaitCount=0 ;
|
|
|
|
return (mo_CCondData.FiniCond()==ZNsEnum::ZEThread_OK) ?
|
|
ZNsEnum::ZEBarrier_OK : ZNsEnum::ZEBarrier_NO ;
|
|
}/*
|
|
int Fini()*/
|
|
|
|
bool Wait()
|
|
{
|
|
bool VB_IsOK=false;
|
|
|
|
mo_CCondData.Lock();
|
|
{
|
|
if(--mi_WaitCount<=0)
|
|
VB_IsOK=(mo_CCondData.WakeAllCond()==ZNsEnum::ZEThread_OK);
|
|
else
|
|
VB_IsOK=(mo_CCondData.WaitCond ()==ZNsEnum::ZEThread_OK);
|
|
//else
|
|
}
|
|
mo_CCondData.UnLock();
|
|
|
|
return VB_IsOK;
|
|
}/*
|
|
bool Wait()*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TData=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCBarrier : public TData /////////*/
|
|
|
|
|
|
#endif // !defined(__USE_XOPEN2K) || defined(__USE_MUTEX_COND_FOR_BARRIER__)
|
|
|
|
|
|
typedef ZCSpinLockEasy ZCFastLockEasy;
|
|
|
|
}/*
|
|
namespace ZNsMain*/
|
|
|
|
|
|
|
|
#ifdef __INC_GLIB_ATOMIC_EXCHANGE__
|
|
|
|
|
|
#include <glib/gatomic.h>
|
|
|
|
|
|
namespace ZNsMain
|
|
{
|
|
|
|
/*///////////////////////////////////////////////////////
|
|
|
|
■ class ZtCAtomicIntSync<>
|
|
|
|
커널이나 라이브러리의 동기화 object 를 사용하지 않고
|
|
동기화를 구현하고 싶은 경우에 사용하는 클래스 템플릿.
|
|
volatile gint 에 대한 원자적 연산을 이용하고 있다.
|
|
|
|
일종의 lock-free 다.
|
|
|
|
■ int sched_yield(void) : IN <sched.h>
|
|
|
|
A process can relinquish the processor voluntarily
|
|
without blocking by calling sched_yield().
|
|
The process will then be moved to the end of the queue
|
|
for its static priority and a new process gets to run.
|
|
|
|
Note: If the current process is the only process
|
|
in the highest priority list at that time,
|
|
this process will continue
|
|
to run after a call to sched_yield().
|
|
|
|
POSIX systems on which sched_yield() is available define _POSIX_PRIORITY_SCHEDULING in <unistd.h>.
|
|
|
|
RETURN VALUE
|
|
|
|
On success, sched_yield() returns 0.
|
|
On error, -1 is returned, and errno is set appropriately.
|
|
|
|
■ 리눅스에서 정수의 atomic 연산 함수는 아래 명령으로 조회할 수 있다.
|
|
|
|
find /usr/include/ | xargs grep 'atomic' | grep 'add' # or
|
|
find /usr/include/ | xargs grep 'atomic' | grep 'increment'
|
|
|
|
■ memory barrier 관련 코드는 아래와 같다.
|
|
|
|
[root@localhost ~]# find /usr/include/ | xargs grep -n 'MEM_BARRIER' | more
|
|
|
|
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:155: _GLIBCXX_READ_MEM_BARRIER;
|
|
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:156: _GLIBCXX_WRITE_MEM_BARRIER;
|
|
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:175: _GLIBCXX_READ_MEM_BARRIER;
|
|
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:176: _GLIBCXX_WRITE_MEM_BARRIER;
|
|
/usr/include/c++/4.1.1/x86_64-redhat-linux/bits/atomic_word.h:42:// #define _GLIBCXX_READ_MEM_BARRIER __asm __volatile ("":::"memory")
|
|
/usr/include/c++/4.1.1/x86_64-redhat-linux/bits/atomic_word.h:46:// #define _GLIBCXX_WRITE_MEM_BARRIER __asm __volatile ("":::"memory")
|
|
/usr/include/c++/4.1.1/bits/atomicity.h:53:#ifndef _GLIBCXX_READ_MEM_BARRIER
|
|
/usr/include/c++/4.1.1/bits/atomicity.h:54:#define _GLIBCXX_READ_MEM_BARRIER __asm __volatile ("":::"memory")
|
|
/usr/include/c++/4.1.1/bits/atomicity.h:56:#ifndef _GLIBCXX_WRITE_MEM_BARRIER
|
|
/usr/include/c++/4.1.1/bits/atomicity.h:57:#define _GLIBCXX_WRITE_MEM_BARRIER __asm __volatile ("":::"memory")
|
|
|
|
■ glib 에 있는 아래 함수를 이용한다. (glib 는 gtk+ 용 라이브러리이다.)
|
|
|
|
gboolean g_atomic_int_compare_and_exchange
|
|
(
|
|
volatile gint *atomic,
|
|
gint oldval ,
|
|
gint newval
|
|
);
|
|
//gboolean g_atomic_int_compare_and_exchange
|
|
|
|
Compares oldval with the integer pointed to by atomic and if they are equal,
|
|
atomically exchanges *atomic with newval. Also acts as a memory barrier.
|
|
|
|
Returns : TRUE, if *atomic was equal oldval. FALSE otherwise
|
|
|
|
□ 아래 함수도 참고한다.
|
|
|
|
gboolean g_atomic_pointer_compare_and_exchange
|
|
(
|
|
volatile gpointer *atomic,
|
|
gpointer oldval,
|
|
gpointer newval
|
|
);
|
|
//gboolean g_atomic_pointer_compare_and_exchange
|
|
|
|
■ -- 2010-04-03 18:05:00
|
|
|
|
■ 리눅스 등에서 ZtCAtomicIntSync<> 이 동기화 object 를 사용해서
|
|
(무겁게) 구현되는 경우, ZtCAtomicIntSync<>::TypeSync 타입을 통해서
|
|
어떤 동기화 object 를 사용했는지를 나타낼 것이다.
|
|
|
|
-- 2010-04-17 21:29:00
|
|
|
|
■ 리눅스에서는 alsa/iatomic.h 에 atomic 관련 함수가 있기는 한데,
|
|
glib 의 g_atomic_int_compare_and_exchange() 에 해당하는 함수가 없다.
|
|
|
|
-- 2011-06-19 22:49:00
|
|
|
|
■ (리눅스 centos)alsa/iatomic.h 에서 atomic_add(), atomic_sub() 등의 함수를 볼 수 있다.
|
|
|
|
static __inline__ void atomic_add(int i, atomic_t *v)
|
|
{
|
|
__asm__ __volatile__(
|
|
ATOMIC_SMP_LOCK "addl %1,%0"
|
|
:"=m" (v->counter)
|
|
:"ir" (i), "m" (v->counter));
|
|
}
|
|
|
|
static __inline__ void atomic_sub(int i, atomic_t *v)
|
|
{
|
|
__asm__ __volatile__(
|
|
ATOMIC_SMP_LOCK "subl %1,%0"
|
|
:"=m" (v->counter)
|
|
:"ir" (i), "m" (v->counter));
|
|
}
|
|
|
|
-- 2013-06-09 00:16:00
|
|
|
|
///////////////////////////////////////////////////////*/
|
|
|
|
|
|
template< typename TTypeBase=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCAtomicIntSync : public TTypeBase
|
|
{
|
|
public :
|
|
typedef ZtCAtomicIntSync TypeSync;
|
|
public :
|
|
enum{ZEUseAtomicInt=1};
|
|
public :
|
|
|
|
enum ZESync
|
|
{
|
|
ZESync_Lock =0,
|
|
ZESync_UnLock=1
|
|
};/*
|
|
enum ZESync*/
|
|
|
|
private:
|
|
volatile gint mi_SyncState;
|
|
public :
|
|
|
|
ZtCAtomicIntSync()
|
|
{
|
|
mi_SyncState=ZESync_UnLock;
|
|
}/*
|
|
ZtCAtomicIntSync()*/
|
|
|
|
ZtCAtomicIntSync(const TTypeBase& rhs) : TTypeBase(rhs)
|
|
{
|
|
mi_SyncState=ZESync_UnLock;
|
|
}/*
|
|
ZtCAtomicIntSync(const TTypeBase& rhs)*/
|
|
|
|
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs) : TTypeBase(rhs)
|
|
{
|
|
mi_SyncState=ZESync_UnLock;
|
|
}/*
|
|
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)*/
|
|
|
|
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)
|
|
{
|
|
this->TTypeBase::operator=(rhs); return *this;
|
|
}/*
|
|
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)*/
|
|
|
|
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)
|
|
{
|
|
this->TTypeBase::operator=(rhs); return *this;
|
|
}/*
|
|
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)*/
|
|
|
|
void Lock()
|
|
{
|
|
#define __INTERLOCKED_COMP_EXCHANGE__ \
|
|
::g_atomic_int_compare_and_exchange(&mi_SyncState, ZESync_UnLock, ZESync_Lock)
|
|
|
|
while(__INTERLOCKED_COMP_EXCHANGE__==FALSE)
|
|
{
|
|
// 아직 Lock 이 걸려 있는 경우이다.
|
|
|
|
::sched_yield();
|
|
}/*
|
|
while(__INTERLOCKED_COMP_EXCHANGE__==FALSE)*/
|
|
|
|
#undef __INTERLOCKED_COMP_EXCHANGE__
|
|
}/*
|
|
void Lock()*/
|
|
|
|
void UnLock()
|
|
{
|
|
mi_SyncState=ZESync_UnLock;
|
|
}/*
|
|
void UnLock()*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TTypeBase=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCAtomicIntSync : public TTypeBase ///*/
|
|
|
|
}/*
|
|
namespace ZNsMain */
|
|
|
|
|
|
#else // !__INC_GLIB_ATOMIC_EXCHANGE__
|
|
|
|
|
|
namespace ZNsMain
|
|
{
|
|
|
|
template< typename TTypeBase=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCAtomicIntSync : public TTypeBase
|
|
{
|
|
public :
|
|
typedef ZCThreadMutexEasy TypeSync;
|
|
public :
|
|
enum{ZEUseAtomicInt=0};
|
|
private:
|
|
TypeSync mo_CSyncEasy;
|
|
public :
|
|
|
|
ZtCAtomicIntSync()
|
|
{
|
|
}/*
|
|
ZtCAtomicIntSync()*/
|
|
|
|
ZtCAtomicIntSync(const TTypeBase& rhs) : TTypeBase(rhs)
|
|
{
|
|
}/*
|
|
ZtCAtomicIntSync(const TTypeBase& rhs)*/
|
|
|
|
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs) : TTypeBase(rhs)
|
|
{
|
|
}/*
|
|
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)*/
|
|
|
|
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)
|
|
{
|
|
}/*
|
|
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)*/
|
|
|
|
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)
|
|
{
|
|
this->TTypeBase::operator=(rhs); return *this;
|
|
}/*
|
|
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)*/
|
|
|
|
void Lock()
|
|
{
|
|
mo_CSyncEasy.Lock();
|
|
}/*
|
|
void Lock()*/
|
|
|
|
void UnLock()
|
|
{
|
|
mo_CSyncEasy.UnLock();
|
|
}/*
|
|
void UnLock()*/
|
|
|
|
public:
|
|
};/*
|
|
template< typename TTypeBase=ZNsMain::ZCEmpty
|
|
>
|
|
class ZtCAtomicIntSync : public TTypeBase ///*/
|
|
|
|
}/*
|
|
namespace ZNsMain */
|
|
|
|
|
|
#endif //!__INC_GLIB_ATOMIC_EXCHANGE__
|
|
|
|
|
|
#endif //__ZCPPMAIN__PROCESS_LINUX_H__
|