'atomic'에 해당되는 글 1건

메모리의 Bus lock을 lock이라는 어셈블리 명령어를 사용하여
C언어만으로는 만들수 없는 Lock을 구현한것입니다.

이 소스는 SMP에서도 잘 돌아갈것입니다.
이유는 Memory의 bus를 lock하는 것이기 때문에 가능한 것입니다.

x86계열 호환입니다. 코드는 VC++ 또는 gcc 로 컴파일 가능합니다.

*** 예외상황 정리 ***
1. lock상호간의 race condition(경쟁조건)
만약 A,B 두개의 lock 키를 다룬다고 하죠.
그리고 (a), (b) 두개의 thread가 움직입니다.
이때 (a)에서는 Lock(A)->Lock(B)->Unlock(B)->Unlock(A) 순으로 lock을 걸겠습니다.
그런데 (b)에서는 Lock(B)->Lock(A)->Unlock(A)->Unlock(B)로 lock을 건다고
상황을 만들어보세요.
과연 이 두개의 thread는 완전히 race condition을 극복할수 있다고 생각하십니까?

2. Dead lock (헤어나지 못하는 조건)
Lock(A)->Lock(A)->Unlock(A)->Unlock(A) 이런 상황은 당연히 벗어날수 없는
무한 lock상태에 돌입한다는 것은 누구나 쉽게 떠오를것입니다.
하지만 코딩하다보면 심심치 않게 버그로 남는 상황이 이러한 것입니다.

3. Kernel의 특권 ! 불가피한 dead lock 회피
CLI->STI만으로 CPU가 몇백개가 병별로 처리되더라도 벗어날수 있는 특권!
하지만 잘 안사용하겠죠?
성능저하가 상상초월.
하지만 CLI->do{ ...->SLEEP ON->... }while(isok)->STI는 좀 낮겠군요.
의도는 아시겠죠? 비선점형에서 선점형으로 진입하는...

코드:

/*
Code by JaeHyuk Cho <mailto:minzkn@infoeq.com> Made in KOREA
http://minzkn.pe.ky
*/

#ifndef DEF_SOURCE_lock_c
#define DEF_SOURCE_lock_c "lock.c"

#include <stdio.h>
#include <sched.h>

#define __DEF_FCTYPE__

static int __DEF_FCTYPE__ __MZ_Lock__(int * volatile s_LockFlag, int s_Switch);
static int __DEF_FCTYPE__ __MZ_AtomicExchange__(int * volatile s_To, int s_Value);

int MZ_InitLock(int * volatile s_Key);
void MZ_Lock(int * volatile s_Key);
void MZ_UnLock(int * volatile s_Key);

static int __DEF_FCTYPE__ __MZ_Lock__(int * volatile s_LockFlag, int s_Switch)
{
int s_Return;
int s_LockTime = 0, s_TimeOut;
s_TimeOut = (s_Switch >> 2) * 100;
L_SpinLoop:;
s_Return = __MZ_AtomicExchange__(s_LockFlag, s_Switch);
if(s_Switch != 0 && s_Return != 0)
{
  if(s_TimeOut != 0 && s_LockTime > s_TimeOut)
  {
   fprintf(stderr, "%s: %s - [ERROR] Dead-lock instead of off (%d second timeout) !!!\n", __FILE__, __FUNCTION__, s_TimeOut *
100);
   s_Return = __MZ_AtomicExchange__(s_LockFlag, 0);
  }
  else
  {
   sched_yield();
   s_LockTime++;
   goto L_SpinLoop;
  }
}
return(s_Return);
}

static int __DEF_FCTYPE__ __MZ_AtomicExchange__(int * volatile s_To, int s_Value)
{
register int s_Return;
#ifdef WIN32
MZ_ASM
{
  PUSH EBX;
  MOV EAX, s_Value;
  MOV EBX, s_To;
  LOCK XCHG DWORD PTR [EBX], EAX;
  POP EBX;
}
#else
MZ_ASM(
  "\n\t"
  "lock xchgl %1, (%2)\n\t"
  "\n\t"
  : "=a"(s_Return)
  : "a"(s_Value), "r"(s_To)
);
#endif
return(s_Return);
}

int MZ_InitLock(int * volatile s_Key)
{
if(s_Key)MZ_UnLock(s_Key);
return(0);
}

void MZ_Lock(int * volatile s_Key)
{
(void)__MZ_Lock__(s_Key, 1);
}

void MZ_UnLock(int * volatile s_Key)
{
(void)__MZ_Lock__(s_Key, 0);
}
#endif

/* End of source */



코드:
/*
  Copyright (C) Information Equipment co.,LTD
  All rights reserved.
  Code by JaeHyuk Cho <mailto:minzkn@infoeq.com>
  CVSTAG="$Header$"
*/

#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sched.h>

#include <pthread.h>

#define def_test_threads (8)
#define def_use_lock (1)

#if defined(__i386__) || defined(_M_IX86)
# define __def_mz_i386__
#endif
#if defined(__powerpc__) || defined(_M_PPC)
# define __def_mz_ppc__
#endif
#if defined(__mips__) || defined(_M_MRX000)
# define __def_mz_mips__
#endif

static char g_mz_architecture[ 1 << 10 ] = {"not defined"};
static int g_mz_ctrl_break = 0;
int g_mz_lock = 0;

int (mz_atomic_exchange)(int * volatile s_to, int s_value)
{
#if defined(__def_mz_i386__)
 (void)strcpy((char *)(&g_mz_architecture[0]), "i386");
 __asm__ __volatile__("xchgl (%2), %0\n\t" : "=r"(s_value) : "0"(s_value), "r"(s_to));
 return(s_value);
#elif defined(__def_mz_ppc__)
 int s_temp;
 (void)strcpy((char *)(&g_mz_architecture[0]), "ppc");
 __asm__ __volatile__(
  "0:\n\t"
  "lwarx %0,0,%1\n\t"
  "stwcx. %2,0,%1\n\t"
  "bne- 0b\n\t"
  : "=&r"(s_temp)
  : "r"(s_to),"r"(s_value)
  : "cr0", "memory"
 );
 return(s_temp);
#elif defined(__def_mz_arm__) /* not tested */
 int s_temp;
 (void)strcpy((char *)(&g_mz_architecture[0]), "arm");
 __asm__ __volatile__(
  "swp %0, %1, [%2]"
  : "=r"(s_value)
  : "0"(s_value), "r"(s_to)
  : "memory"
 );
 return(s_temp);
#elif defined(__def_mz_mips__)
 int s_result, s_temp;
 (void)strcpy((char *)(&g_mz_architecture[0]), "mips");
 __asm__ __volatile__(
  "1:\n\t"
  ".set push\n\t"
  ".set mips2\n\t"
  "ll %0,%3\n\t"
  "move %1,%4\n\t"
  "beq %0,%4,2f\n\t"
  "sc %1,%2\n\t"
  ".set pop\n\t"
  "beqz %1,1b\n"
  "2:\n\t"
  : "=&r" (s_result), "=&r" (s_temp), "=m" (*s_to)
  : "m" (*s_to), "r" (s_value)
  : "memory");
 return(s_result);
#else /* unknown architecture */
 static pthread_mutex_t sg_mz_mutex_bus = PTHREAD_MUTEX_INITIALIZER;
 int s_temp, s_check;
 (void)strcpy((char *)(&g_mz_architecture[0]), "unknown");
 pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, (void *)(&sg_mz_mutex_bus));
 s_check = pthread_mutex_lock((pthread_mutex_t *)(&sg_mz_mutex_bus));
 s_temp = *(s_to); *(s_to) = s_value;
 s_check = pthread_mutex_unlock((pthread_mutex_t *)(&sg_mz_mutex_bus));
 pthread_cleanup_pop(0);
 return(s_temp);
#endif
}

void (mz_load_balance)(void)
{
 sched_yield();
}

void (mz_unlock)(int *s_this)
{
 (void)mz_atomic_exchange(s_this, 0);
}

void (mz_lock)(int *s_this)
{
 l_lock_wait:;
 if(mz_atomic_exchange(s_this, 1) == 0)return;
 mz_load_balance();
 goto l_lock_wait;
}

void (mz_signal)(int s_signal)
{
 switch(s_signal)
 {
  case SIGINT: case SIGTERM:
       if(g_mz_ctrl_break == 0)(void)fputs("\nbreak.\x1b[0m\n", stdout);
       g_mz_ctrl_break = 1;
       (void)signal(s_signal, mz_signal);
       break;
  case SIGSEGV: case SIGFPE:
       g_mz_ctrl_break = 1;
       (void)fputs("\nOOPS: segment fault\x1b[0m\n", stderr);
       (void)raise(s_signal);
       exit(0);
  default: break;
 }
}

void * (mz_test_thread)(void *s_argument)
{
 int s_pid = (int)getpid();
 (void)fprintf(stdout, "run thread %d\n", s_pid);
 do
 {
#if def_use_lock != (0)
  mz_lock((int *)(&g_mz_lock));
#endif
  *((int *)s_argument) = 0;
  mz_load_balance();
  *((int *)s_argument) = s_pid;
#if def_use_lock != (0)
  mz_unlock((int *)(&g_mz_lock));
#endif
  mz_load_balance();
 }while(g_mz_ctrl_break == 0);       
 return(s_argument);
}

int (main)(void)
{
 pthread_t s_pthread[ def_test_threads ];
 int s_index;
 volatile int s_test_value = (int)getpid();
 (void)signal(SIGINT, mz_signal);
 (void)signal(SIGTERM, mz_signal);
 (void)signal(SIGSEGV, mz_signal);
 (void)signal(SIGFPE, mz_signal);
 (void)mz_atomic_exchange((int *)(&g_mz_lock), 0);
 (void)fprintf(stdout, "architecture=%s\n", (char *)(&g_mz_architecture[0]));
 for(s_index = 0;s_index < def_test_threads;s_index++)
 {
  if(pthread_create((pthread_t *)(&s_pthread[s_index]), (pthread_attr_t *)0, mz_test_thread, (void *)(&s_test_value)) != 0)
  {
   (void)fputs("pthread_create error\n", stderr);
   break;
  }
 }
 (void)fprintf(stdout, "threads=%d\n", s_index);
 while(g_mz_ctrl_break == 0)
 {
#if def_use_lock != (0)
  mz_lock((int *)(&g_mz_lock));
#endif
  if(s_test_value == 0)(void)fprintf(stdout, "\n\x1b[1;31munsafe value = %d\x1b[0m\n", s_test_value);
  else
  {
   (void)fprintf(stdout, "\rsafe value = %d", s_test_value);
   (void)fflush(stdout);
  }
#if def_use_lock != (0)
  mz_unlock((int *)(&g_mz_lock));
#endif
  mz_load_balance();
 }
 while((--s_index) >= 0)(void)pthread_join(s_pthread[s_index], (void **)0);
 (void)fputs("\n\x1b[0mExit.\n", stdout);
 return(0);
}

/* vim: set expandtab: */
/* End of source */
크리에이티브 커먼즈 라이센스
Creative Commons License
Posted by minzkn

트랙백 주소 :: http://blog.minzkn.com/trackback/150

댓글을 달아 주세요