LoginSignup
1
0

More than 3 years have passed since last update.

EventQueueを使ってみたよ (4)

Last updated at Posted at 2019-12-30

前回の記事では、EventQueueについてThe EventQueue API Tutorialでイベントループを使うところまで読んでみましたが、プログラムに問題があることがわかりました。今回は、問題点について考えます。

題材について

この記事で題材として取り上げたのは、The EventQueue APIというTutorialです。

イベントループを使うときに考えられる問題

原文には、この見出しはありません。私が付けたものです。

前回の記事からプログラムを再掲します。

#include "mbed.h"

DigitalOut led1(LED1);
InterruptIn sw(USER_BUTTON, PullUp);
EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread thread;

void rise_handler(void) {
    printf("rise_handler in context %p\r\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

void fall_handler(void) {
    printf("fall_handler in context %p\r\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

int main() {
    // Start the event queue
    thread.start(callback(&queue, &EventQueue::dispatch_forever));
    printf("Starting in context %p\r\n", ThisThread::get_id());
    // The 'rise' handler will execute in IRQ context
    sw.rise(rise_handler);
    // The 'fall' handler will execute in the context of thread 't'
    sw.fall(queue.event(fall_handler));
}

The code for rise_handler is problematic because it calls printf in interrupt context, which is a potentially unsafe operation.

rise_handlerのコードには問題があります。割り込みcontextの中からprintfを呼び出している事で潜在的に安全でない操作を行っているからです。

Fortunately, this is exactly the kind of problem that event queues can solve.

幸いなことに、これはまさしくイベントキューによって解決される種類の問題です。

We can make the code safe by running rise_handler in user context (like we already do with fall_handler) by replacing this line:

このコードは、すでにfall_handlerでそうしたように、rise_handlerをユーザcontextで実行することによって安全にすることができます。以下のコードを

sw.rise(rise_handler);

with this line:

このように書き換えます。

sw.rise(queue.event(rise_handler));

この文書では、「rise_handlerを割り込みcontextから呼び出すのは安全ではない。」としていますが、なぜ安全ではないかについては述べられていません。ここで少し考えてみます。

安全ではないとされていたのはprintfの呼び出しです。printfは、複数のスレッドから呼び出される可能性があり、しかも出力する表示に乱れがあってはならないために、一度に複数の要求を受け付けないようにしなくてはなりません。このような場合に使われるのが、セマフォ(semaphore)とかミューテックス(mutex)と呼ばれる仕組みです。この仕組みを使うと、あるスレッドでprintfを実行している間は、他のスレッドでprintfを実行できないようにすることができます。printfの処理をする立場から考えると安全です。

ところが、このprintfをISRで使ってしまうと問題が発生します。もし、他のスレッドでprintfが実行中であった場合、ISRの処理はprintfを呼び出したところで、現在実行中のprintfの処理が終わるまで待たされます。しかし、printfを実行中の他のスレッドは割り込みが発生した時点で停止しているため、printfの処理は永遠に終わりません。こうして、デッドロックの状態に陥ってしまうのです。

この文書で作成されたプログラム例でも、ユーザcontextでfall_handlerprintfを実行中に、rise_handlerがISRから呼び出されるとデッドロックが発生するはずです。ボタンを素早く押し続けていると、そういった状況になる可能性があります。

こういった問題が発生しないように、現在のMbed OSでは、割り込みcontextでのMutexの操作が禁止されたのだろうと考えられます。

このプログラムには、まだ問題がありそうですが、次回に続きます。

関連サイト

Mbed OSのページ
EventQueueのTutorial
Mbed対応Cypress製品のページ

関連記事

EventQueueを使ってみたよ (1)
EventQueueを使ってみたよ (2)
EventQueueを使ってみたよ (3)
EventQueueを使ってみたよ (4)
EventQueueを使ってみたよ (5)
EventQueueを使ってみたよ (6)
EventQueueを使ってみたよ (7)
EventQueueを使ってみたよ (8)
EventQueueを使ってみたよ (9)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0