nonblock형 코드를 block형으로 변경방법

lmk378의 이미지

다음과 같은 코드가 있습니다.

void a_request();
void a_response();

void b_request();
void b_response();

a_request()를 호출해서 성공시 a_response()가 콜백되고
b_request() 호출 성공시 b_response()가 콜백됩니다.
request 함수들은 다 nonblock형으로 동작합니다.
문제는 a_request()가 호출되고 성공적으로 a_response()가 콜백되고 return되어야
b_request()가 진행될 수 있습니다. 즉 a_response에 b_request하는 코드를 입력해도 정상동작하지 않습니다.
이런경우로 인해 a_request() 함수를 block형태로 동작하게 하고싶습니다.(즉 a_response()가 호출되기 전까지 a_requst()가 block 되도록)
현재로 생각한 방법은 locking variable을 두어서 while로 스핀락을 거는 방법 뿐입니다.
스핀락 말고 좀더 세련된 방법이 없을까요?

lmk378의 이미지

아시는분 안계신가요?

익명 사용자의 이미지

어떤 쓰레딩 라이브러리를 사용하고 계신지 모르겠습니다만 아마도 join 함수가 있을 겁니다.
b_request 쓰레드가 a_request 쓰레드가 끝날 때까지 기다려야하는 상황이면
b_request 쓰레드가 a_request 쓰레드에 join을 걸면 됩니다.

스핀락을 어떤 의미로 사용하신 건지 모르겠는데
기본적인 동기화 도구들에 대해서 알고계신지요.
혹시 모르고 계시다면 우선
mutex, semaphore, conditional variable
등에 대해서 먼저 알아보세요.

lmk378의 이미지

질문을 좀더 구체화해서 설명드리겠습니다.
아래 코드를 보시면

void main()
{
a_request();
b_request();
}

void a_request()
{
HTTP http;
http.send(IP, "hello");
http.response_callback(a_response);
}

void a_response(char* data)
{
print(data);
}

void b_request()
{
HTTP http;
http.send(IP, "world");
http.response_callback(b_response);
}

void b_response(char* data)
{
print(data);
}

위와같은 코드라 생각하시면 됩니다.
즉 a_request와 b_request는 동일한 쓰레드라 생각하시면 되고
a_response가 되고나서 b_request를 진행시키고 싶습니다.
(즉 request 함수들을 response가 오기 전까지 return되지 않는(block)형태로 동작시키고 싶습니다.)
쓰레드간의 동기화를 위해 스핀락을 사용한거구요.. 뮤텍스 라이브러리는 사용하진 않았지만 뮤텍스를 이용한 스핀락이라 보시면 될것 같습니다.
(크리티컬 섹션이 없기때문에 뮤텍스락이 올바른 표현은 아닐 것 같습니다)
익명님의 말대로 pthread_join을 사용할 수가 없는게 callback function이 언제 발생할지 알 수 없고 발생하더라도 thread id를 알 수 없기때문입니다.
질문이 좀 난해했나요? request 쓰레드에서 response 쓰레드를 만드는 것이 아닙니다. 그것은 라이브러리가 해주는 것이죠.

정리해서 말씀드리면 기존 nonblock 모드로 동작하는 socket http 라이브러리를
block 모드로 동작하는 것처럼 세련되게 코딩하는 법이 있는가 해서 질문했습니다.
현재는 a_response 콜백함수 내에 b_request를 넣어서 동작은 시킵니다만 추후 반대의 동작(b_response가 수행된 후 a_request를 보내고 싶다면)이
필요할 경우 문제가 되기 때문입니다.

peecky의 이미지

Queue request_queue;
 
void process_request_queue() {
	functype * callback = request_queue.dequeue();
	functype * request = request_queue.dequeue();
 
	if (callback) callback();
	if (request) {
		request();
		http.response_callback(process_request_queue);
	}
}
 
void main() {
	request_queue.enqueue(NULL);
	request_queue.enqueue(a_request);
	request_queue.enqueue(a_response);
	request_queue.enqueue(b_request);
	request_queue.enqueue(b_response);
	request_queue.enqueue(NULL);
	process_request_queue();
}
shint의 이미지

ㅡ_ㅡ;;

다른 방법도 있습니다.
- 가장 쉬운 방법. 응답에 조건문 주기 A면 B. B면 A. (같은 함수를 호출해서도 가능합니다.)
- 조금 고민할 내용. 응답된 데이터나 함수를 큐'등에 특정 공간에 저장한후 순서대로 뽑아서 출력하기
- 이건 좀 황당한 방법... 타이머로 주기적으로 상태를 비교하며 호출하기

글을 좀 읽기 좋게 정리 해 봤습니다.
A요청
A응답

B요청
B응답

A요청을 호출해서 성공시 A응답이 콜백되고.
B요청을 호출해서 성공시 B응답이 콜백됩니다.

요청함수들은 다 논블럭형으로 동작합니다.

문제는
A요청이 호출된후 성공적으로 A응답이 콜백되어 return되야
B요청이 진행될 수 있습니다. 즉 A응답에 B요청을 코드로 입력해도 정상동작하지 않습니다.
이런경우로 인해 A요청함수를 block 형태로 동작하게 만들고 싶습니다.
즉 A응답이 호출되기 전까지 A요청이 block되도록 말이죠.

현재 생각한 방법은 locking variable을 두어서 while로 스핀락을 거는 방법 뿐 입니다.
스핀락 말고 좀 더 세련된 방법이 없을까요?

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

kukyakya의 이미지

condition variable에 wait를 걸고 기다리고, response 콜백 함수에서 해당 condition variable에 notify를 날리면 될 것 같습니다.

mutex m;
condition_variable cond;
 
void main()
{
a_request();
b_request();
}
 
void a_request()
{
  HTTP http;
  http.send(IP, "hello");
  http.response_callback(a_response);
 
  lock l(m);
  cond.wait(l);
}
 
void a_response(char* data)
{
  print(data);
  cond.notify();
}
 
void b_request()
{
  HTTP http;
  http.send(IP, "world");
  http.response_callback(b_response);
 
  lock l(m);
  cond.wait(l);
}
 
void b_response(char* data)
{
  print(data);
  cond.notify();
}
gilgil의 이미지

[sample code]

VEvent event;
void main()
{
	a_request();
	b_request();
}
 
void a_request()
{
	HTTP http;
	http.send(IP, "hello");
	event.wait(); // 다른 데에서 event를 깨워 줄 때까지 기다림
}
 
void a_response(char* data)
{
	print(data);
	event.setEvent(); // event를 깨워 줌
}
 
void b_request()
{
	HTTP http;
	http.send(IP, "world");
	event.wait();
}
 
void b_response(char* data)
{
	print(data);
	event.setEvent();
}

[header file] - Qt를 사용하였지만, QMutex, QWaitCondition 등은 linux로 쉽게 포팅할 수 있음

class VEvent
{
protected:
  QMutex         m_mutex;
  QWaitCondition m_cond;
  bool           m_manualReset;
  bool           m_state;
 
public:
  VEvent(bool manualReset = false, bool initialState = false);
  virtual ~VEvent();
 
public:
  bool state() { return m_state; }
  void setEvent();
  void resetEvent();
  bool wait(unsigned long timeout = ULONG_MAX);
};

[geshifilter-code]
VEvent::VEvent(bool manualReset, bool initialState)
{
  m_manualReset = manualReset;
  m_state       = initialState;
}

VEvent::~VEvent()
{
}

void VEvent::setEvent()
{
  m_mutex.lock();
  m_state = true;
  m_cond.wakeAll();
  m_mutex.unlock();
}

void VEvent::resetEvent()
{
  m_state = false;
}

bool VEvent::wait(unsigned long timeout)
{
  m_mutex.lock();
  bool res = true;
  if (!m_state)
  {
    res = m_cond.wait(&m_mutex, timeout);
  }
  if (!m_manualReset) m_state = false;
  m_mutex.unlock();
  return res;
}
[/geshifilter-code]
furmuwon의 이미지

세련되게 함수로 한번 만들어 보시면 어떨까요?
재미 있는 주제여서 커널에서 한번 만들어 보았습니다~

struct reqres {
	struct list_head item;
	int number;
	int (*request)(int (*resp)(void *));
	int (*respond)(void *);
};
 
struct blk_que {
	struct list_head list;
	spinlock_t list_lock;
	struct reqres *process;
};
 
int seq_number = 0;
struct blk_que *mainque = NULL;
static DECLARE_COMPLETION(resp_comp);
static DECLARE_COMPLETION(req_comp);
 
int blk_que_add(int (*arequest)(int (*resp)(void *)), int (*arespond)(void *))
{
	struct reqres *addq;
 
	if (!mainque)
		return -ESRCH;
 
	addq = (struct reqres *)kzalloc(sizeof(struct reqres), GFP_KERNEL);
	addq->request = arequest;
	addq->respond = arespond;
	addq->number = seq_number;
	INIT_LIST_HEAD(&addq->item);
	spin_lock(&mainque->list_lock);
	list_add_tail(&addq->item, &mainque->list);
	spin_unlock(&mainque->list_lock);
	printk(KERN_INFO "(%-4d)pid: Add Que %d\n", current->pid, seq_number);
	complete(&req_comp);
	return seq_number++;
};
 
static int blk_que_respond(void *data)
{
	struct reqres *cur_proc = NULL;
	int found = 0;
	int ret = 0;
	spin_lock(&mainque->list_lock);
	if (!list_empty(&mainque->list)) {
		list_for_each_entry(cur_proc, &mainque->list, item) {
			if (cur_proc == mainque->process) {
				found = 1;
				break;
			}
		}
	}
	spin_unlock(&mainque->list_lock);
 
	if (found == 1) {
		printk(KERN_INFO "(%-4d)pid: Found mainque process %d\n", current->pid, mainque->process->number);
		list_del(&cur_proc->item);
		ret = cur_proc->respond(data);
		mainque->process = NULL;
		kfree(cur_proc);
		complete(&resp_comp);
	} else
		printk(KERN_INFO "(%-4d)pid: Not found mainque process\n", current->pid);
 
	return ret;
}
 
static int blk_que_kthread(void *data)
{
	struct reqres *cur_proc;
	while (!kthread_should_stop()) {
		wait_for_completion_interruptible(&req_comp);
		cur_proc = NULL;
		spin_lock(&mainque->list_lock);
		if (!list_empty(&mainque->list)) {
			list_for_each_entry(cur_proc, &mainque->list, item) {
				break;
			}
		}
		spin_unlock(&mainque->list_lock);
		if (!mainque->process) {
			printk(KERN_INFO "(%-4d)pid: start req %d\n", current->pid, cur_proc->number);
			mainque->process = cur_proc;
			cur_proc->request(blk_que_respond);
			wait_for_completion_interruptible(&resp_comp);
		} else {
			printk(KERN_INFO "(%-4d)pid: current another que processing\n", current->pid);
			schedule();
			complete(&req_comp);
		}
	};
 
	return 0;
}
 
static int blk_que_init(void)
{
	int err = 0;
	if (mainque)
		goto blk_que_init_exit;
 
	blkq_th = kthread_run(blk_que_kthread, NULL, MODULE_NAME);
	if (IS_ERR(blkq_th)) {
		err = PTR_ERR(blkq_th);
		goto blk_que_init_exit;
	}
	mainque = (struct blk_que *)kzalloc(sizeof(struct blk_que), GFP_KERNEL);
	INIT_LIST_HEAD(&mainque->list);
	mainque->process = NULL;
	spin_lock_init(&mainque->list_lock);
blk_que_init_exit:
	return err;
}

Test code

#if 1 //TEST
 
void a_async_res(void *data, async_cookie_t cookie) {
	int (*resp)(void *) = data;
	resp("Complete A");
}
 
int a_request(int (*resp)(void *)) {
	async_cookie_t scookie;
	printk(KERN_INFO "(%-4d)pid: a_request\n", current->pid);
	scookie = async_schedule(a_async_res, resp);
}
 
int a_respond(void *data) {
	printk(KERN_INFO "(%-4d)pid: a_respond data:%s\n", current->pid, (char *)data);
}
 
void b_async_res(void *data, async_cookie_t cookie) {
	int (*resp)(void *) = data;
	resp("Complete B");
}
 
int b_request(int (*resp)(void *)) {
	async_cookie_t scookie;
	printk(KERN_INFO "(%-4d)pid: b_request\n", current->pid);
	scookie = async_schedule(b_async_res, resp);
}
 
int b_respond(void *data) {
	printk(KERN_INFO "(%-4d)pid: b_respond data:%s\n", current->pid, (char *)data);
}
 
void test_blk_que(void) {
	int i;
	for (i = 0; i < 5; i++) {
		blk_que_add(a_request, a_respond);
		blk_que_add(b_request, b_respond);
	}
}
 
#endif //TEST

결과

[  115.834091] (1220)pid: Add Que 60
[  115.837317] (1220)pid: Add Que 61
[  115.837331] (1045)pid: start req 60
[  115.837333] (1045)pid: a_request
[  115.847328] (52  )pid: Found mainque process 60
[  115.851831] (52  )pid: a_respond data:Complete A
[  115.851862] (1220)pid: Add Que 62
[  115.851866] (1220)pid: Add Que 63
[  115.851870] (1220)pid: Add Que 64
[  115.851874] (1220)pid: Add Que 65
[  115.851878] (1220)pid: Add Que 66
[  115.851881] (1220)pid: Add Que 67
[  115.851885] (1220)pid: Add Que 68
[  115.851889] (1220)pid: Add Que 69
[  115.851894] blk_que blk_que: blkq_show
[  115.886560] (1045)pid: start req 61
[  115.890000] (1045)pid: b_request
[  115.893265] (52  )pid: Found mainque process 61
[  115.897726] (52  )pid: b_respond data:Complete B
[  115.902365] (1045)pid: start req 62
[  115.905797] (1045)pid: a_request
[  115.909024] (52  )pid: Found mainque process 62
[  115.913534] (52  )pid: a_respond data:Complete A
[  115.918138] (1045)pid: start req 63
[  115.925172] (1045)pid: b_request
[  115.928380] (52  )pid: Found mainque process 63
[  115.932973] (52  )pid: b_respond data:Complete B
[  115.937512] (1045)pid: start req 64
[  115.940979] (1045)pid: a_request
[  115.944199] (52  )pid: Found mainque process 64
[  115.948694] (52  )pid: a_respond data:Complete A
[  115.953322] (1045)pid: start req 65
[  115.956767] (1045)pid: b_request
[  115.959995] (52  )pid: Found mainque process 65
[  115.964501] (52  )pid: b_respond data:Complete B
[  115.969106] (1045)pid: start req 66
[  115.972574] (1045)pid: a_request
[  115.975793] (52  )pid: Found mainque process 66
[  115.980290] (52  )pid: a_respond data:Complete A
[  115.984914] (1045)pid: start req 67
[  115.988361] (1045)pid: b_request
[  115.991600] (52  )pid: Found mainque process 67
[  115.996087] (52  )pid: b_respond data:Complete B
[  116.000744] (1045)pid: start req 68
[  116.004159] (1045)pid: a_request
[  116.007388] (52  )pid: Found mainque process 68
[  116.011895] (52  )pid: a_respond data:Complete A
[  116.016500] (1045)pid: start req 69
[  116.019956] (1045)pid: b_request
[  116.023194] (52  )pid: Found mainque process 69
[  116.027682] (52  )pid: b_respond data:Complete B

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.