既然是同步机制,主要的工作就是调用了这个函数,程序就会等另外的事件完成之后再继续下面的工作。
wait_for_completion 结构体/*
* struct completion - structure used to maintain state for a "completion"
*
* This is the opaque structure used to maintain the state for a "completion".
* Completions currently use a FIFO to queue threads that have to wait for
* the "completion" event.
*
* See also: complete(), wait_for_completion() (and friends _timeout,
* _interruptible, _interruptible_timeout, and _killable), init_completion(),
* reinit_completion(), and macros DECLARE_COMPLETION(),
* DECLARE_COMPLETION_ONSTACK().
*/
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
结构体里面就两个东西,一个是 done,一个是队列wait。
还有他相关的函数
complete()
wait_for_completion()
init_completion()
reinit_completion()
DECLARE_COMPLETION()
DECLARE_COMPLETION_ONSTACK().
如何使用?
先声明
struct completion bl_ready;
init_completion(&ts->bl_ready);
需要等待退出的线程
static irqreturn_t cyttsp_irq(int irq, void *handle)
{
struct cyttsp *ts = handle;
int error;
/*退出的时候会设置为CY_BL_STATE*/
/*在这里做判断*/
if (unlikely(ts->state == CY_BL_STATE)) {
complete(&ts->bl_ready);
goto out;
}
}
退出时候调用
static int cyttsp_soft_reset(struct cyttsp *ts)
{
unsigned long timeout;
int retval;
/* wait for interrupt to set ready completion */
reinit_completion(&ts->bl_ready);
/*退出的时候会设置为 CY_BL_STATE*/
ts->state = CY_BL_STATE;
enable_irq(ts->irq);
retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
if (retval)
goto out;
timeout = wait_for_completion_timeout(&ts->bl_ready,
msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
retval = timeout ? 0 : -EIO;
out:
ts->state = CY_IDLE_STATE;
disable_irq(ts->irq);
return retval;
}
里面的实现细节
三个函数的流程
init_completion 初始化我们看init 初始化的代码
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
static inline void reinit_completion(struct completion *x)
{
x->done = 0;
}
初始化的时候,把done这个变量设置为 0 。
wait_for_completion_timeout 的调用流程static inline long __sched
do_wait_for_common(struct completion *x,
long (*action)(long), long timeout, int state)
{
/*如果done是0就跑到if里面去,也就说明我们等的事件还没完成*/
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);
__add_wait_queue_tail_exclusive(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = action(timeout);/*休眠timeout时间等待*/
spin_lock_irq(&x->wait.lock);
} while (!x->done && timeout);/*如果事件完成了就提出*/
__remove_wait_queue(&x->wait, &wait);
if (!x->done)
return timeout;
}
x->done--;
return timeout ?: 1;
}
内核里面有非常多这样的小代码,我觉得看这样的代码非常有意思,你需要不断的去揣摩它,品味它。
这段代码的主要工作就是在超时时间内判断done的值已经大于0,然后退出,退出的时候把done 减去1。
总结文章里面的实例是从一个touch驱动里面提取出来的,它的目的是为了reset触摸屏的时候,确保中断线程已经完全退出了,在中断线程里面有操作touch I2C的操作,如果reset的时候,线程还没有跑完,驱动就会可能收到I2C报错的异常。
error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
pdata->name, ts);
内核完成补丁
这份补丁是用来修复do_wait_for_common 这个函数的,看注释的作用就是用一行代码代替了几行代码。内核专家做了修正,贴出来方便大家加深理解。
Change do_wait_for_common() to use signal_pending_state() instead of
open coding.
Signed-off-by: Oleg Nesterov <oleg@xxxxxxxxxx>
--- 26-rc2/kernel/sched.c~1_SPS_WAIT_FOR 2008-07-22 18:36:58.000000000 +0400
+++ 26-rc2/kernel/sched.c 2008-07-24 19:54:12.000000000 +0400
@@ -4735,10 +4735,7 @@ do_wait_for_common(struct completion *x,
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&x->wait, &wait);
do {
- if ((state == TASK_INTERRUPTIBLE &&
- signal_pending(current)) ||
- (state == TASK_KILLABLE &&
- fatal_signal_pending(current))) {
+ if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
推荐阅读:
专辑|Linux文章汇总
专辑|程序人生