Slot 탐색 및 Page 저장

Minjun_·2022년 1월 30일
0

CUBRID

목록 보기
4/8
post-thumbnail

1. 분석 문서

Slot 탐색 및 Page 저장은 Slot 탐색을 진행하고, Page를 해당 순서의 Slot에 저장하는 메커니즘이다.

1-1. Block 및 Slot 의 위치 탐색

  • DWB의 전역변수 ‘position_with_flags‘를 비트 연산을 사용해서 Block 및 Slot의 index 탐색한다.
  • 이를 사용하여서 Slot의 시작주소를 획득한다.

    Block의 index는 순환적이다.

1-2. Page 저장

  • 구한 Slot의 index에 Page 저장(memory copy)한다.
    * 실제로는 Block의 지역변수 write_buffer에 slot순서대로 저장
  • Vpid, LSA정보를 함께 저장한다.

Slot 탐색 및 Page 저장

1-3. 탐색 및 저장 실패 케이스

해당 케이스는 slot의 index를 찾기 전에 해당 block에 다른 thread가 write 작업을 진행 중일 때 나타나는 현상이다.
여기서 write 작업이란 다른 thread가 해당 block에 대해 slot 탐색 ~ DWB flush 사이의 작업이 진행중임을 의미한다.

Flush 순서 및 대기

  1. Page를 저장해야 할 slot이 첫 번째 slot이고, block이 다른 thread에 의해 writing 중인 상태
  2. 해당 work thread를 대기 상태로 설정하려고 할 때, block의 지역변수에 존재하는 wait_queue에 work thread를 저장시키고 대기
  3. 해당 block이 writing 상태가 끝날 때까지 work thread는 dwb_aquire_next_slot() 함수 내부 start goto phase부터 다시 진행
  4. 해당 block에 대해 다른 thread가 write 작업 마쳤다면, 다시 함수 내부에서 처음부터 시작할 때, 현재 thread가 전역변수 position_with_flags에서 현재 block의 상태를 writing으로 바꾼 다음 다시 slot의 index 찾기 작업 진행

2. 코드 분석

dwb_add_page

/*
* dwb_add_page ()		: DWB에 페이지 컨텐츠 추가
*
* 반환					: Error code
* thread_p (인자)		: Thread entry
* io_page_p(인자)		: 페이지의 현재 내용이 있는 메모리 내 주소
* vpid (인자)			: 페이지 식별자
* p_dwb_slot (인자)		: 페이지 콘텐츠를 추가해야 하는 DWB slot
*
* ※ Flush thread를 사용할 수 없거나 독립 실행형(stand alone)인 경우 thread가 block을 flush할 수 있다.
*/
int dwb_add_page(THREAD_ENTRY *thread_p, FILEIO_PAGE *io_page_p, VPID *vpid, DWB_SLOT **p_dwb_slot)
{
	unsigned int count_wb_pages;	// write buffer에 추가된 페이지 수
	int error_code = NO_ERROR;		// #define NO_ERROR 0
	bool inserted = false;			// DWB에 slot이 잘 추가되었는지 확인하는 bool 타입 변수
	DWB_BLOCK *block = NULL;		// block 포인터
	DWB_SLOT *dwb_slot = NULL;		// slot 포인터
	bool needs_flush;				// flush가 필요한지 확인하는 bool 타입 변수 (count_wb_pages >= DWB 각 block의 page 수 → flush)

	assert(p_dwb_slot != NULL && (io_page_p != NULL || (*p_dwb_slot)->io_page != NULL) && vpid != NULL);
	// 위 조건에 만족하지 않으면 crash

	if (thread_p == NULL)
	{
		thread_p = thread_get_thread_entry_info();
		// 인자로 thread_p가 제대로 들어오지 않았다면 위 함수를 통해 자체적으로 thread entry 구함
	}

	if (*p_dwb_slot == NULL)	// *p_dwb_slot이 NULL이라면
	{
		error_code = dwb_set_data_on_next_slot(thread_p, io_page_p, true, p_dwb_slot);
		// 가능한 경우, next DWB slot에 데이터 set / error code 설정
		// dwb_acquire_next_slot()에서 position_with_flags에서 현재 block num, 다음 slot의 위치를 구함
		// 현재 block의 slot pointer + 구한 slot의 index로 slot의 pointer를 구하고 p_dwb_slot에 대입
		// dwb_set_slot_data()에서 p_dwb_slot에 io_page_p를 입력
		if (error_code != NO_ERROR)
		{
			return error_code;
			// 위 함수 안에서 에러 발생 시 add_page 중단 / error_code 반환
		}

		if (*p_dwb_slot == NULL)
		{
			return NO_ERROR;
			// 위 함수를 거치고도 아직 *p_dwb_slot이 NULL이라면 add_page 중단 / NO_ERROR 반환
		}
	}

	dwb_slot = *p_dwb_slot;
	함수 내에서 선언한 dwb_slot 포인터 변수에 *p_dwb_slot 대입

	assert(VPID_EQ(vpid, &dwb_slot->vpid));
	// #define VPID_EQ(vpid_ptr1, vpid_ptr2) ((vpid_ptr1) == (vpid_ptr2) || ((vpid_ptr1)->pageid == (vpid_ptr2)->pageid && (vpid_ptr1)->volid == (vpid_ptr2)->volid))
	// vipd와 slot->vpid를 비교하는 이유는 dwb_set_slot_data() 함수에서 입력 과정에서 error가 발생됐는지 check하기 위함으로 추측됨
	// vpid와 &dwb_slot->vpid가 일치하지 않으면 crash

	if (!VPID_ISNULL(vpid))		// vpid가 NULL이 아니면
	{
		error_code = dwb_slots_hash_insert(thread_p, vpid, dwb_slot, &inserted);
		// slots hash에 삽입 진행 / error code 설정
		if (error_code != NO_ERROR)
		{
			return error_code;
			// 위 함수 안에서 에러 발생 시 add_page 중단 / error_code 반환
		}

		if (!inserted)			// 삽입 진행 후에도 inserted 변수가 아직 false라면
		{
			VPID_SET_NULL(&dwb_slot->vpid);
			// 동일한 데이터를 두 번 flush하지 않도록 slot을 NULL로 세팅해 무효화 시킴
			fileio_initialize_res(thread_p, dwb_slot->io_page, IO_PAGESIZE);
			// LSA를 NULL로 세팅 / io_page 구조체 내의 변수들 default값으로 초기화
		}
	}

	dwb_log("dwb_add_page: added page = (%d,%d) on block (%d) position (%d)\n", vpid->volid, vpid->pageid,
			dwb_slot->block_no, dwb_slot->position_in_block);
	/*
	*	#define dwb_log(...) \
	*		if (dwb_Log)     \
	*		_er_log_debug(ARG_FILE_LINE, "DWB: " __VA_ARGS__)
	*/
	// dwb_Log가 설정되어있으면 error log file에 인자로 넘긴 메세지 출력

	block = &dwb_Global.blocks[dwb_slot->block_no];
	// 함수 내에서 선언한 block 포인터 변수에 DWB 전역 구조체 내의 블록 배열의 slot의 block 번호 주소 대입
	count_wb_pages = ATOMIC_INC_32(&block->count_wb_pages, 1);
	// 함수 내에서 선언한 count_wb_pages에 ATOMIC_INC_32로 &block->count_wb_pages 대입
	// write_buffer의 page가 1개 추가되었으므로 +1 해서 대입
	assert_release(count_wb_pages <= DWB_BLOCK_NUM_PAGES);
	// count_wb_pages가 DWB 각 block의 page 수보다 크면 crash
	// assert()와 assert_release()의 차이는..?

	if (count_wb_pages < DWB_BLOCK_NUM_PAGES)	// count_wb_pages가 DWB 각 block의 page 수보다 작으면
	{
		needs_flush = false;
		// flush 불필요
	}
	else
	{
		needs_flush = true;
		// flush 필요
	}

	if (needs_flush == false)
	{
		return NO_ERROR;
		// flush 할 필요 없으면 add_page 중단 / NO_ERROR 반환
	}
	/*
	* 궁금한점
	* 위의 과정을 진행하는 이유는 block이 가득찼을 경우에만 flush하는 것인데 왜 needs_flush를 사용해야 할까?
	* 그냥 첫 if문에서 page 수보다 작으면 return (NO_ERROR)로 빠져나오면 될텐데 굳이 needs_flush 변수를 사용해야 할까?
	* needs_flush가 다른 부분에서 사용되면 모르겠는데 여기서만 사용됨
	*/


	/*
	* Block은 일관된 데이터를 갖기 위해 채워진 순서대로 flush 되어야 한다.
	* Flush block thread는 채워진 순서대로 block을 flush하는 방법을 알고 있다.
	* 따라서 여기에선 flushing 순서에 대해 더 이상 신경 쓰지 않는다.
	* 최초에는 이전 block이 flush되지 않은 경우 여기에서 기다렸었다. 이러한 접근 방식으로 인해 지연이 발생됐었다.
	*/


// SEVER_MODE로 실행됐을 경우에만 진행
#if defined(SERVER_MODE)
	/*
	* 현재 block을 flush하기 위해 flush block thread를 깨운다.
	* 현재 block은 이전 block을 flush한 후 flush된다.
	*/
	if (dwb_is_flush_block_daemon_available())		// flush block daemon이 가능한 상태이면
	{
		dwb_flush_block_daemon->wakeup();
		// block을 flush할 thread를 깨움

		return NO_ERROR;
	}
#endif /* SERVER_MODE */


	error_code = dwb_flush_block(thread_p, block, false, NULL);
	// 현재 block에서 모든 pages flush / error code 설정
	if (error_code != NO_ERROR)
	{
		dwb_log_error("Can't flush block = %d having version %lld\n", block->block_no, block->version);
		
		return error_code;
		// flush 실패 시 log에 에러 메세지 남기고 error code 반환
	}

	dwb_log("Successfully flushed DWB block = %d having version %lld\n", block->block_no, block->version);

	return NO_ERROR;
	// flush 성공 시 해당 메세지를 log에 성공 메세지 남기고 함수 종료
}

thread_get_thread_entry_info

inline cubthread::entry *
thread_get_thread_entry_info (void)
{
  cubthread::entry &te = cubthread::get_entry ();
  return &te;
}

DWB Flag

typedef struct double_write_buffer DOUBLE_WRITE_BUFFER;
struct double_write_buffer
{
  unsigned int num_blocks;
> 블록의 전체 개수
  
  unsigned int num_pages;
> 페이지의 전체 개수
  
  unsigned int num_block_pages;
> 블록당 페이지 수
  
  unsigned int log2_num_block_pages;
> log2(블록당 페이지 수)

  pthread_mutex_t mutex;
> wait queue 변경 시 사용될 lock
  DWB_WAIT_QUEUE wait_queue;
> wait queue

  UINT64 volatile position_with_flags;
}

log2_num_block_pages

num_blocks(블록의 전체 개수) 와 num_pages(페이지의 전체 개수) 는 모두 2^n

IO_PAGESIZE = 16 KB 기준

32 <= num_pages <= 2048
32 <= 2^(5~11) <= 2048

1 <= num_blocks <= 32
1 <= 2^(0~5) <= 32


num_block_pages = num_pages(2^a) / num_blocks(2^b)

따라서 num_block_pages는 2^n (n >= 0) 이 됩니다.
때문에 log2_num_block_pages는 n (n >= 0) 이 됩니다.

2진수에서 값이 2^n라는 것은 하나의 비트만 SET 되어있다는 것을 의미합니다.
따라서 log2_num_block_pages 만큼 비트를 미는 것으로 깔끔하게 쓰고잇는 현재 블록을 얻을 수 있습니다.

값이 64인 변수를 예로 들면

log2 64 = 6

64:
0100 0000

64 >> log2 64 = 0000 0001

position_with_flags

제목 없음-2




dwb_set_data_on_next_slot

진입점

dwb_set_data_on_next_slot (thread_p, io_page_p, can_wait, p_dwb_slot)
=> dwb_add_page
=> pgbuf_bcb_flush_with_wal 또는 fileio_write_or_add_to_dwb 또는 dwb_flush_force
dwb_set_data_on_next_slot (thread_p, io_page_p, can_wait, p_dwb_slot)
=> pgbuf_bcb_flush_with_wal

dwb_set_data_on_next_slot

현재 슬롯을 반환해 값을 넣고 다음 슬롯이 있다면 다음 슬롯을 물려줌

return         : 에러 코드
io_page_p      : 슬롯에 놓게 될 데이터
can_wait       : 처리 도중 대기가 허용되는 지. true이면 허용
p_dwb_slot     : 슬롯 포인터의 포인터
int
dwb_set_data_on_next_slot (THREAD_ENTRY * thread_p, FILEIO_PAGE * io_page_p, bool can_wait, DWB_SLOT ** p_dwb_slot)
{
  int error_code;

  assert (p_dwb_slot != NULL && io_page_p != NULL);

  error_code = dwb_acquire_next_slot (thread_p, can_wait, p_dwb_slot);
> 페이지를 놓을 슬롯을 찾아 *p_dwb_slot 에 넣어줍니다

  if (error_code != NO_ERROR)
    {
      return error_code;
    }

  assert (can_wait == false || *p_dwb_slot != NULL);
> can_wait가 false 이거나,
> can_wait가 true이면서 *p_dwb_slot이 NULL은 아니어야 함
  
  if (*p_dwb_slot == NULL)
    {
      return NO_ERROR;
    }
> can_wait가 false이고, *p_dwb_slot이 NULL인 경우 처리에 사용할 슬롯을 찾을 수 없었던 것이므로 종료


  dwb_set_slot_data (thread_p, *p_dwb_slot, io_page_p);
> 찾은 슬롯에 페이지 놓기

  return NO_ERROR;
}


dwb_acquire_next_slot

값을 넣을 슬롯을 가져옵니다

return         : 에러 코드
can_wait       : 처리 도중 대기가 허용되는 지. true이면 허용
p_dwb_slot     : 슬롯 포인터의 포인터
STATIC_INLINE int
dwb_acquire_next_slot (THREAD_ENTRY * thread_p, bool can_wait, DWB_SLOT ** p_dwb_slot)
{
  UINT64 current_position_with_flags, current_position_with_block_write_started, new_position_with_flags;
  unsigned int current_block_no, position_in_current_block;
  int error_code = NO_ERROR;
  DWB_BLOCK *block;

  assert (p_dwb_slot != NULL);
  *p_dwb_slot = NULL;

start:
> 시작점

  current_position_with_flags = ATOMIC_INC_64 (&dwb_Global.position_with_flags, 0ULL);
> current_position_with_flags = dwb_Global.position_with_flags

  if (DWB_NOT_CREATED_OR_MODIFYING (current_position_with_flags))
> dwb가 만들어지지 않았고, 만들어지는 중이라면
    {
      /* Rarely happens. */
      if (DWB_IS_MODIFYING_STRUCTURE (current_position_with_flags))
> 만들어지는 중이라면
	{
	  if (can_wait == false)
> (dwb가 만들어질 때까지) 대기가 불가능하다면
	    {
	      return NO_ERROR;
> 종료
	    }

	  error_code = dwb_wait_for_strucure_modification (thread_p);
> dwb 가 만들어질 때까지 대기
	  
	  if (error_code != NO_ERROR)
> 에러 발생
	    {
	      if (error_code == ER_CSS_PTHREAD_COND_TIMEDOUT)
		{
		  goto start;
> 타임아웃일 경우 위에서부터 재시도
		}
	      return error_code;
> 다른 에러일 경우 에러 반환
	    }

	  goto start;
> 다시 제일 위 조건부터 플래그 검증
	}
      else if (!DWB_IS_CREATED (current_position_with_flags))
> 만들어지지 않았으면
	{
	  if (DWB_IS_ANY_BLOCK_WRITE_STARTED (current_position_with_flags))
> 블록이 하나라도 WRITE_STARTED 상태라면 (0~31 번째 비트 중 하나라도 SET 이라면)

	    {
	      /* Someone deleted the DWB, before flushing the data. */
	      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DWB_DISABLED, 0);
	      return ER_DWB_DISABLED;
> 에러처리
	    }

	  /* Someone deleted the DWB */
	  return NO_ERROR;
> DWB 가 사용불가하므로 종료
	}
      else
	{
	  assert (false);
	}
    }

  current_block_no = DWB_GET_BLOCK_NO_FROM_POSITION (current_position_with_flags);
> current_block_no = (current_position_with_flags & DWB_POSITION_MASK) >> dwb_Global.log2_num_block_pages

  position_in_current_block = DWB_GET_POSITION_IN_BLOCK (current_position_with_flags);
> position_in_current_block = current_position_with_flags & DWB_POSITION_MASK & (dwb_Global.num_block_pages - 1)


ex)
num_block_pages: 64
log2_num_block_pages: 6

current_position_with_flags & DWB_POSITION_MASK 을 통해 block 번호와 slot index를 알아낼 수 있음
해당 값이 0 이면 블록 번호 0 // 64 = 0, 슬롯 인덱스 0 % 64 = 0
해당 값이 65 이면 블록 번호 65 // 64 = 1, 슬롯 인덱스 65 % 64 = 1

이 과정을 비트로 나타내면 아래와 같습니다

current_position_with_flags & DWB_POSITION_MASK => 1이라고 가정 => 00 0000 0000 0000 0000 0000 0000 0001
블록 번호: 0000 0001 >> 6 = 0
슬롯 인덱스: 0000 0001 & 63 = 0000 0001 & 0011 1111 = 1

current_position_with_flags & DWB_POSITION_MASK => 65라고 가정 => 00 0000 0000 0000 0000 0000 0100 0001
블록 번호: 0100 0001 >> 6 = 1
슬롯 인덱스: 0100 0001 & 0011 1111 = 1



  assert (current_block_no < DWB_NUM_TOTAL_BLOCKS && position_in_current_block < DWB_BLOCK_NUM_PAGES);

  if (position_in_current_block == 0)
> 처리하게 될 슬롯이 0번째 슬롯일 때
    {
      /* This is the first write on current block. Before writing, check whether the previous iteration finished. */
      if (DWB_IS_BLOCK_WRITE_STARTED (current_position_with_flags, current_block_no))
> current_position_with_flags의 MSB에서 current_block_no 번째 비트가 SET 되어 있으면 =
> current_block_no 블록이 쓰기 작업 중이면
	{
	  if (can_wait == false)
	    {
	      return NO_ERROR;
> 대기가 불가능하면 종료
	    }

	  dwb_log ("Waits for flushing block=%d having version=%lld) \n",
		   current_block_no, dwb_Global.blocks[current_block_no].version);


	  error_code = dwb_wait_for_block_completion (thread_p, current_block_no);
> 버퍼가 덮어씌워지는 것을 피하기 위해서 이전 쓰기 작업이 끝날 때까지 대기
> dwb_wait_for_strucure_modification 와 체크 플래그만 다르고 나머진 동일

	  if (error_code != NO_ERROR)
	    {
	      if (error_code == ER_CSS_PTHREAD_COND_TIMEDOUT)
		{
		  goto start;
> timeout의 경우 처음부터 재시도
		}

	      dwb_log_error ("Error %d while waiting for flushing block=%d having version %lld \n",
			     error_code, current_block_no, dwb_Global.blocks[current_block_no].version);
	      return error_code;
> 다른 에러의 경우 에러처리
	    }

	  goto start;
> 대기가 끝난 이후 플래그 검토를 위해 처음부터 시작
	}

      assert (!DWB_IS_BLOCK_WRITE_STARTED (current_position_with_flags, current_block_no));

      current_position_with_block_write_started =
	DWB_STARTS_BLOCK_WRITING (current_position_with_flags, current_block_no);
> current_position_with_block_write_started = current_position_with_flags의 MSB에서 current_block_no 번째 비트를 SET

      new_position_with_flags = DWB_GET_NEXT_POSITION_WITH_FLAGS (current_position_with_block_write_started);
> new_position_with_flags = slot의 index가 (num_pages - 1) 과 같다면 0, 아니라면 slot을 1 증가하고 플래그를 반환합니다
> 위 방식으로 슬롯을 순회합니다

    }
  else
    {
      assert (DWB_IS_CREATED (dwb_Global.position_with_flags));
      assert (!DWB_IS_MODIFYING_STRUCTURE (dwb_Global.position_with_flags));

      new_position_with_flags = DWB_GET_NEXT_POSITION_WITH_FLAGS (current_position_with_flags);
> new_position_with_flags = slot의 index가 (num_pages - 1) 과 같다면 0, 아니라면 slot을 1 증가하고 플래그를 반환합니다
> 위 방식으로 슬롯을 순회합니다

    }


  if (!ATOMIC_CAS_64 (&dwb_Global.position_with_flags, current_position_with_flags, new_position_with_flags))
> 만약 다른 스레드에서 처리가 이뤄져 플래그가 바뀌었다면, 다시 처음부터 시작합니다.
> 그게 아니라면 dwb_Global.position_with_flags에 새 플래그를 대입합니다
    {
      goto start;
    }

  block = dwb_Global.blocks + current_block_no;
> block = 포인터 dwb_Global.blocks에 현재 블록 번호를 더한 포인터

  *p_dwb_slot = block->slots + position_in_current_block;
> *p_dwb_slot = 해당 블록의 슬롯 포인터에 슬롯 위치를 더한 포인터

  /* Invalidate slot content. */
  VPID_SET_NULL (&(*p_dwb_slot)->vpid);

  assert ((*p_dwb_slot)->position_in_block == position_in_current_block);

  return NO_ERROR;
> 
}



dwb_wait_for_strucure_modification

STATIC_INLINE DWB_WAIT_QUEUE_ENTRY *
dwb_make_wait_queue_entry (DWB_WAIT_QUEUE * wait_queue, void *data)
{
  DWB_WAIT_QUEUE_ENTRY *wait_queue_entry;

  assert (wait_queue != NULL && data != NULL);

  if (wait_queue->free_list != NULL)
    {
      wait_queue_entry = wait_queue->free_list;
      wait_queue->free_list = wait_queue->free_list->next;
      wait_queue->free_count--;
    }
> freelist 가 존재한다면 freelist에서 가져와서 사용
  else
    {
      wait_queue_entry = (DWB_WAIT_QUEUE_ENTRY *) malloc (sizeof (DWB_WAIT_QUEUE_ENTRY));
> 없다면 직접 할당

      if (wait_queue_entry == NULL)
	{
	  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DWB_WAIT_QUEUE_ENTRY));
	  return NULL;
	}
> 에러처리
    }

  wait_queue_entry->data = data;
> data는 스레드 포인터
  wait_queue_entry->next = NULL;
  return wait_queue_entry;
> 블록 초기화 및 반환
}


STATIC_INLINE DWB_WAIT_QUEUE_ENTRY *
dwb_block_add_wait_queue_entry (DWB_WAIT_QUEUE * wait_queue, void *data)
{
  DWB_WAIT_QUEUE_ENTRY *wait_queue_entry = NULL;

  assert (wait_queue != NULL && data != NULL);

  wait_queue_entry = dwb_make_wait_queue_entry (wait_queue, data);
> wait queue에 들어갈 대기열 생성

  if (wait_queue_entry == NULL)
    {
      return NULL;
> 할당 오류
    }

  if (wait_queue->head == NULL)
    {
      wait_queue->tail = wait_queue->head = wait_queue_entry;
> wait_queue가 비어있으면 head = tail = wait_queue_entry
    }
  else
    {
      wait_queue->tail->next = wait_queue_entry;
      wait_queue->tail = wait_queue_entry;
> tail->next에 해당 블록을 넣고 tail을 마지막 블록으로 정리
    }
  wait_queue->count++;
> count + 1

  return wait_queue_entry;
}



STATIC_INLINE int
dwb_wait_for_strucure_modification (THREAD_ENTRY * thread_p)
{
#if defined (SERVER_MODE)
  int error_code = NO_ERROR;
  DWB_WAIT_QUEUE_ENTRY *double_write_queue_entry = NULL;
  UINT64 current_position_with_flags;
  int r;
  struct timeval timeval_crt, timeval_timeout;
  struct timespec to;
  bool save_check_interrupt;

  (void) pthread_mutex_lock (&dwb_Global.mutex);
> wait queue의 수정에 앞서 lock

  current_position_with_flags = ATOMIC_INC_64 (&dwb_Global.position_with_flags, 0ULL);
> current_position_with_flags = dwb_Global.position_with_flags

  if (!DWB_IS_MODIFYING_STRUCTURE (current_position_with_flags))
> 불필요한 대기를 피하기 위해 DWB 가 생성중이 아니라면
    {
      pthread_mutex_unlock (&dwb_Global.mutex);
      return NO_ERROR;
> lock을 해제하고 종료
    }

  thread_lock_entry (thread_p);
> 해당 스레드도 lock

  double_write_queue_entry = dwb_block_add_wait_queue_entry (&dwb_Global.wait_queue, thread_p);
> wait queue에 대기열 추가
  if (double_write_queue_entry == NULL)
    {
      /* allocation error */
      thread_unlock_entry (thread_p);
      pthread_mutex_unlock (&dwb_Global.mutex);

      ASSERT_ERROR_AND_SET (error_code);
      return error_code;
> 실패했으면 에러처리
    }

  pthread_mutex_unlock (&dwb_Global.mutex);
> 스레드 unlock

  save_check_interrupt = logtb_set_check_interrupt (thread_p, false);

  gettimeofday (&timeval_crt, NULL);
  timeval_add_msec (&timeval_timeout, &timeval_crt, 10);
  timeval_to_timespec (&to, &timeval_timeout);
> timeout을 위한 설정

  r = thread_suspend_timeout_wakeup_and_unlock_entry (thread_p, &to, THREAD_DWB_QUEUE_SUSPENDED);
> 실질적 대기, 내부에서 pthread_cond_timedwait 함수를 호출하여 대기가 일어남
> pthread_cond_timedwait는 리눅스 함수이므로, 윈도우의 경우에는 동일 이름으로 함수를 구현하여 제공

  (void) logtb_set_check_interrupt (thread_p, save_check_interrupt);
  if (r == ER_CSS_PTHREAD_COND_TIMEDOUT)
    {
      /* timeout, remove the entry from queue */
      dwb_remove_wait_queue_entry (&dwb_Global.wait_queue, &dwb_Global.mutex, thread_p, NULL);
      return r;
> timeout 처리

    }
  else if (thread_p->resume_status != THREAD_DWB_QUEUE_RESUMED)
    {
      assert (thread_p->resume_status == THREAD_RESUME_DUE_TO_SHUTDOWN);

      dwb_remove_wait_queue_entry (&dwb_Global.wait_queue, &dwb_Global.mutex, thread_p, NULL);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INTERRUPTED, 0);
      return ER_INTERRUPTED;
> 인터럽트가 일어났을 경우 처리

    }
  else
    {
      assert (thread_p->resume_status == THREAD_DWB_QUEUE_RESUMED);
      return NO_ERROR;
> 종료

    }
#else /* !SERVER_MODE */
  return NO_ERROR;
#endif /* !SERVER_MODE */
}



dwb_set_slot_data

slot이 가리키는 위치에 데이터를 놓습니다

return         : 에러 코드
dwb_slot       : slot 위치가 되는 포인터
io_page_p      : 데이터 페이지
STATIC_INLINE void
dwb_set_slot_data (THREAD_ENTRY * thread_p, DWB_SLOT * dwb_slot, FILEIO_PAGE * io_page_p)
{
  assert (dwb_slot != NULL && io_page_p != NULL);

  assert (io_page_p->prv.p_reserve_2 == 0);

  if (io_page_p->prv.pageid != NULL_PAGEID)
> 데이터 페이지의 페이지가 유효하다면 
    {
      memcpy (dwb_slot->io_page, (char *) io_page_p, IO_PAGESIZE);
> 슬롯의 페이지 위치에 해당 페이지 복사
    }
  else
    {
      /* Initialize page for consistency. */
      fileio_initialize_res (thread_p, dwb_slot->io_page, IO_PAGESIZE);
> 슬롯의 페이지 자체를 초기화
    }

  assert (fileio_is_page_sane (io_page_p, IO_PAGESIZE));
  LSA_COPY (&dwb_slot->lsa, &io_page_p->prv.lsa);
  VPID_SET (&dwb_slot->vpid, io_page_p->prv.volid, io_page_p->prv.pageid);
> dwb_slot->vpid.volid = io_page_p->prv.volid
> dwb_slot->vpid.pageid = io_page_p->prv.pageid
}

이 글은 42Seoul에서 진행된 CUBRID DB Engine 스터디로 팀원들과 함께 공부한 내용을 포스팅한 것입니다.
https://github.com/innovationacademy-kr/dbstudy

profile
졸음을 이겨내자..!

0개의 댓글