3.1. Atomic bit operations & spin lock

Multithread ÇÁ·Î±×·¥¿¡¼­ °¡Àå Å« ¹®Á¦°¡ µ¿½Ã¿¡ ½ÇÇàÇÏ´Â ¿©·¯°³ÀÇ thread°£ÀÇ µ¿±âÈ­ÀÔ´Ï´Ù. UPÀÇ °æ¿ì ±âº»ÀûÀ¸·Î ¾î´À ½ÃÁ¡¿¡ controlÀ» ´Ù¸¥ thread·Î ³Ñ±æ Áö¸¦ ¾Ë ¼ö ÀÖÀ¸¹Ç·Î ÇÁ·Î¼¼¼­ÀÇ º°´Ù¸¥ Áö¿ø¾øÀ̵µ µ¿±âÈ­°¡ °¡´ÉÇÏÁö¸¸ MP¿¡¼± µ¿½Ã¿¡ ÀÛµ¿ÇÏ´Â ¿©·¯°³ÀÇ ÇÁ·Î¼¼¼­µéÀ» µ¿±âÈ­½ÃŰ·Á¸é Ư¼öÇÑ ±â´ÉÀ» Á¦°øÇؾßÇÕ´Ï´Ù. ±âº»ÀûÀ¸·Ð ¸Þ¸ð¸®ÀÇ ¾î¶² °ªÀ» Àо Á¶°ÇÀ» È®ÀÎÇÏ°í ±× °ª¿¡ ¾î¶² º¯È­¸¦ ÁÖ´Â ÀÛ¾÷À» atomicÇÏ°Ô ¼öÇàÇÒ ¼ö ÀÖ¾î¾ßÇÕ´Ï´Ù. º¸Åë test and set bitÀ̳ª exchangeµîÀ» atomicÇÏ°Ô ¼öÇàÇÏ´Â ±â´ÉÀ» Á¦°øÇÏ°Ô µÇ´Âµ¥ ix86¿¡¼± µÎ°¡Áö ¸ðµÎ Áö¿øµË´Ï´Ù. (ix86¿¡¼± instruction¿¡ LOCK prefix¸¦ ºÙÀÌ¸é ´ëºÎºÐÀÇ instructionµéÀ» atomicÇÏ°Ô ¼öÇàÇÒ ¼ö ÀÖ½À´Ï´Ù.)

ÀÌ Àý¿¡¼± atomic test and set bitÀ» ÀÌ¿ëÇØ µ¿±âÈ­ÀÇ ±âº»ÀûÀÎ ±¸¼º¿ä¼ÒÀÎ spin lockÀ» ¸¸µé¾îº¸°Ú½À´Ï´Ù. Pthread¸¦ »ç¿ëÇØ ¸î°³ÀÇ threadµé »çÀÌ¿¡¼­ spinÀ» ÀÌ¿ëÇÑ µ¿±âÈ­¸¦ ÇØº¼ÅÙµ¥, °¢°¢ÀÇ threadµéÀÌ µ¶¸³µÈ processor¿¡¼­ ÀÛµ¿ÇÏ´Â °ÍÀÌ ¾Æ´Ï¶ó time sliceµÇ¾î ÀÛµ¿Çϱ⠶§¹®¿¡ spinÀ» »ç¿ëÇÏ´Â °ÍÀº ÁÁÀº »ý°¢Àº ¾Æ´Õ´Ï´Ù. ¿¹¸¦ µé±âÀ§ÇÑ °ÍÀ̶ó°í »ý°¢ÇϽñ⠹ٶø´Ï´Ù.


#include <stdio.h>
#include <pthread.h>
#include <time.h>

static __inline__ int
test_and_set_bit(int nr, volatile void *addr)
{
	int oldbit;

	__asm__ __volatile__(
		"lock			\n\t"
		"btsl	%2, %1		\n\t"
		"sbbl	%0, %0		"
		: "=r" (oldbit), "=m" (*(unsigned *)addr)
		: "Ir" (nr));
	return oldbit;
}

typedef unsigned spin_t;

static __inline__ void
spin_lock(spin_t *spin)
{
	if (test_and_set_bit(0, spin))
	while (*(__volatile__ spin_t *)spin) ;
}

static __inline__ void
spin_unlock(spin_t *spin)
{
	*(__volatile__ spin_t *)spin = 0;
}

spin_t spin = 0;

static __inline__ void
waste_time(void) {
	time_t ltime;

	ltime = time(NULL);
	while (time(NULL) == ltime) ;
}

#define pp(fmt, arg...)	printf("thread %d : "fmt, thread_no , ##arg)

static void *
thread_func(void *_arg)
{
	int thread_no = (int)_arg;

	while (1) {
	pp("wasting time\n");
	waste_time();
	pp("entering critical section\n");
	pp("now in critical section, wasting time\n");
	waste_time();
	pp("leaving critical section\n");
	}
	
	return NULL;
}

static void *
thread_sfunc(void *_arg)
{
	int thread_no = (int)_arg;
	time_t ltime;
	
	while (1) {
		pp("wasting time\n");
		waste_time();
		pp("entering critical section\n");
		spin_lock(&spin);
		pp("now in critical section, wasting time\n");
		waste_time();
		pp("leaving critical section\n");
		spin_unlock(&spin);
	}
	
	return NULL;
}

int
main(void)
{
	pthread_t thread0, thread1, thread2, thread3;
	
	pthread_create(&thread0, NULL, thread_func, (void *)0);
	pthread_create(&thread1, NULL, thread_func, (void *)1);
	pthread_create(&thread2, NULL, thread_sfunc, (void *)2);
	pthread_create(&thread3, NULL, thread_sfunc, (void *)3);
	
	pthread_join(thread0, NULL);
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);
	pthread_join(thread3, NULL);
	
	return 0;
}
      

4°³ÀÇ thread°¡ ½Ã°£º¸³»±â, critical sectionµé¾î°¡±â, ½Ã°£º¸³»±â, critical section³ª¿À±âÀÇ °úÁ¤À» ¹Ýº¹ÇÕ´Ï´Ù. 0¹ø°ú 1¹ø thread´Â ¾Æ¹«·± µ¿±âÈ­µµ ÇÏÁö ¾Ê°í 2, 3Àº spinÀ» ÀÌ¿ëÇØ critical sectionÀ» º¸È£ÇÕ´Ï´Ù.

¿ì¼± test_and_set_bit ÇÔ¼ö¸¦ »ìÆìº¸µµ·Ï ÇϰڽÀ´Ï´Ù.


	lock; btsl %2, %1
      

AtomicÇÏ°Ô *addrÀÇ nr¹øÂ° bitÀ» test and setÇÕ´Ï´Ù. TestÀÇ °á°ú´Â carry flag¿¡ ±â·ÏÀ̵˴ϴÙ.


	sbbl %0, %0
      

À§ÀÇ btsl¿¡¼­ CF±â·ÏµÈ test°á°ú¸¦ %0¿¡ ÀúÀåÇÕ´Ï´Ù. sbblÀº subtraction with carry·Î À§Ã³·³ °°Àº µÎ ¼ö·Î ½ÇÇàÇϸé carry flag°ú °°Àº ³í¸®°ªÀÌ operand¿¡ ÀúÀåµË´Ï´Ù.

Output, input ÁöÁ¤¿¡¼± nrÀÇ constrait°¡ "Ir"ÀÎ Á¡À» Á¦¿ÜÇÏ¸é º°´Ù¸¥ Á¡Àº ¾ø½À´Ï´Ù. nrÀÌ ÇÑ word¾È¿¡¼­ÀÇ bit offsetÀ̹ǷΠimmediateÀÏ ¶§ 0~31»çÀÌ·Î Á¦ÇÑÀ» µÐ °ÍÀ̰í, ·¹Áö½ºÅÍ operandÀÏ ¶§´Â ÄÄÆÄÀÏŸÀÓ¿¡ È®ÀÎÇÒ ¼ö ¾ø±â ¶§¹®¿¡ ±×³É 'r'À» constraint·Î ÁØ °ÍÀÔ´Ï´Ù.

test_and_set_bitÀÌ ÀÖÀ¸¸é spinÀÇ ±¸ÇöÀº °£´ÜÇѵ¥¿ä. test_and_set_bitÀÇ °á°ú°ªÀÌ 0ÀÏ ¶§±îÁö ¹Ýº¹ÀûÀ¸·Î ¼öÇàÇÏ¸é µË´Ï´Ù. À§ÀÇ ÇÁ·Î±×·¥¿¡¼­ ±â´Ù¸®´Â ºÎºÐÀ» while·Î ó¸®ÇÑ °ÍÀº lock; btslº¸´Ù º¸ÅëÀÇ memory read°¡ bus¿¡ ºÎ´ãÀ» ´ú Áֱ⠶§¹®ÀÔ´Ï´Ù. whileÀÇ Á¶°Ç¿¡¼­ __volatile__·Î ij½ºÆÃÈÄ »ç¿ëÇÏ´Â °Ç gcc°¡ ·¹Áö½ºÅÍ¿¡ spinÀÇ °ªÀ» ÀÐÀº ÈÄ ±× °ªÀ» °è¼Ó testÇÏ´Â °ÍÀ» ¸·±â À§Çؼ­ ÀÔ´Ï´Ù. Gcc¿¡°Ô ÀÌ °ªÀº ÇöÀç ÇÁ·Î±×·¥ÀÇ ÁøÇà°ú °ü°è¾øÀÌ ¹Ù²ð ¼ö ÀÖ´Ù´Â °ÍÀ» ¾Ë·ÁÁÖ´Â °ÍÀÔ´Ï´Ù.

UnlockÀº ±×³É spinÀÇ °ªÀ» 0À¸·Î ÇÏ¸é µË´Ï´Ù. ¿ª½Ã memory¿¡ Á÷Á¢¾²µµ·Ï ÇϱâÀ§ÇØ __volatile__·Î ij½ºÆÃÈÄ »ç¿ëÇÏ´Â °ÍÀ» º¼ ¼ö ÀÖ½À´Ï´Ù. ±×³É spinÀ» __volatile__·Î ÁöÁ¤ÇØÁ־ µË´Ï´Ù.

Inline assembly´Â ´Ü¼øÇϹǷΠÄÄÆÄÀÏµÈ assembly´Â »ý·«ÇÏ°í ½ÇÇà °á°ú¸¦ º¸°Ú½À´Ï´Ù.

thread 0 : wasting time
thread 1 : wasting time
thread 2 : wasting time
thread 3 : wasting time
thread 0 : entering critical section
thread 0 : now in critical section, wasting time
thread 3 : entering critical section
thread 3 : now in critical section, wasting time
thread 2 : entering critical section
thread 1 : entering critical section
thread 1 : now in critical section, wasting time
thread 1 : leaving critical section
thread 1 : wasting time
thread 0 : leaving critical section
thread 0 : wasting time
thread 3 : leaving critical section
thread 3 : wasting time
thread 2 : now in critical section, wasting time
thread 2 : leaving critical section
thread 2 : wasting time
thread 1 : entering critical section
thread 1 : now in critical section, wasting time
thread 0 : entering critical section
thread 0 : now in critical section, wasting time
thread 3 : entering critical section
thread 3 : now in critical section, wasting time
thread 2 : entering critical section
thread 1 : leaving critical section
thread 1 : wasting time
thread 0 : leaving critical section
thread 0 : wasting time
thread 3 : leaving critical section
thread 3 : wasting time
thread 2 : now in critical section, wasting time
      

0°ú 1ÀÇ critical sectionÀº °ãÄ¡Áö¸¸ 2¿Í 3Àº °ãÄ¡Áö ¾ÊÀ½À» ¾Ë ¼ö ÀÖ½À´Ï´Ù.