マルチタスク5

Semaphore.h

タスクは共有されている変数を用いてお互いに通信することが出来ます。タスクがオブジェクトにアクセスできるのはstaticあるいはアクセス可能なオブジェクト変数です。問題は、このタイプのアクセスが無制限であることです。いくつかのアプリケーションでは、タスク内通信(intertask, interprocessとも言います)をより制御したい場合があります。

全範囲のタスク内通信メソッドを実装することは可能ですが、mbedはメモリの制限があり、Semaphoreクラスのようなものが必要になります。

Semaphoreクラスの定義は次のようになります。


Semaphore.h
#ifndef _SEMAPHORE
#define _SEMAPHORE

#include "Event.h"
#include "Task.h"
#include "common.h"

/*
 * Semaphore class
 *
 * 資源をコントロールするタスクによって用いられる。
 * 一度に一つのタスクによって取得することができる。
 * 続いて起こるタスクは中断されセマフォリストに加えられる
 */


/**
 * タスクオブジェクトに対しての標準的なセマフォサポート
 * ready状態から開始する
 */

class Semaphore : public Event
{
  protected:
  /**
   * セマフォオブジェクトを作る
   */
  public:
    static Semaphore* semaphoreList ;
    Semaphore* nextSemaphore ;
    Task* acquiredTask ;
    Task* waitingTaskList ;
    Semaphore ();
  /**
   * セマフォがreadyかどうかを調べる
   *
   * 戻り:取得されるセマフォがreadyならばtrue
   */
  bool ready ();
  /**
   * セマフォを取得する。タスクの次の状態を設定する
   *
   * この作用を実現するにはタスクはexecute()を抜けなければならない
   * もしそのタスクがセマフォを条件付で取得しなければならないならば
   * ready()を用いる。
   * もしセマフォがreadyでなかったならば、task.suspend()を実行せよ
   * もしセマフォが取得できたら実行が続けられる。
   * これはもし可能ならばあるアクションを実行した後でセマフォをすぐに
   * タスクが解放することを許します。適切にnextStateを設定する。
   * release()に一つ以上一致することはないことを念頭に入れよ。
   * 例:
   *  case 9: // これはいつもコントロールを与える
   *     semaphore.acquire(11);
   *     break;
   *  case 10: // もし取得されなかったらなばコントロールを与える
   *     if ( semaphore.acquire(11))
   *       break;
   *  case 11:
   *     // 何かをする
   *     semaphore.relese() ;
   *     nextState(12);
   *     break ;
   *
   * 戻り:もしセマフォが取得されたならばfalse
   *       もし中断(suspend)だったらtrue
   */
  bool acquire ( int nextState );
  /**
   * セマフォの解放。一つが待っていれば次のタスクを開始する
   * 解放するタスクはそれをする事が許可されていると仮定する
   */
  void release ();
  /**
   * イベントが起こったときにセマフォを解放する
   * Semaphoreクラスはnotifyメソッドのみを持っている
   * イベントクラスに基づいている
   */
  void notify (Object object);
  /**
   * 名前を得る
   *
   * 戻り:セマフォの名前を返す
   */
  char* name ();
};

#endif
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 


Semaphore.cpp



Semaphore.cpp
#include "Semaphore.h"
Semaphore* Semaphore::semaphoreList = (Semaphore*)null;

Semaphore::Semaphore () {
      nextSemaphore = semaphoreList ;
      semaphoreList = this ;
      acquiredTask = (Task*)null ;
      waitingTaskList = (Task*)null ;
}

bool Semaphore::ready () {
  return ( acquiredTask == (Task*)null ) ;
}

bool Semaphore::acquire ( int nextState ) {
  Task* _currentTask = Task::currentTask ;

  // 次の状態に行かせる
  _currentTask->nextState ( nextState ) ;

  if ( ready ()) {
    acquiredTask = Task::currentTask ;
    return false ;
  }

  // 実行リスト()からタスクを削除する
  if ( ! _currentTask->suspend ()) {
    return true ; // タスクがすでにsuspendしていた。
  }

  // タスクはセマフォ待ちと表す。_を付ける
  _currentTask->__taskStatus = Task::taskSemaphoreWait ;

  // 待ちリストにタスクを加える
  if ( waitingTaskList == (Task*)null ) {
    // リストが空だった
    waitingTaskList = Task::currentTask ;
  } else {
    // リストの最後に付け加える
    // 注意: task.nextTask はnullであるべき
    Task* lastTask = waitingTaskList ;
    // リストの最後に移動
    while ( lastTask->nextTask != (Task*)null ) {
      lastTask = lastTask->nextTask ;
    }

    // リストの最後にタスクを追加する
    lastTask->nextTask = Task::currentTask ;
    Task::currentTask->nextTask = (Task*)null ;
  }
  return true ;
}

void Semaphore::release () {
  if ( waitingTaskList == (Task*)null ) {
    acquiredTask = (Task*)null ;
  }
  else {
    // nextStateセット(集合)でタスクがsuspendしている
    // 最初の待機タスクをResumeする
    acquiredTask = waitingTaskList ;

    waitingTaskList = waitingTaskList->nextTask ;
    acquiredTask->nextTask = (Task*)null ;

    // 仕事をresumeするにはtaskStatusがtaskSuspendedでなければならない
    acquiredTask->__taskStatus = Task::taskSuspended ;
    acquiredTask->resume () ;
  }
}
  
void Semaphore::notify (Object object) {
  release () ;
}
  
char* Semaphore::name () {
  return "Semaphore" ;
}


Semaphoreオブジェクトは一つのタスクによって取得されます。もし二番目のタスクが、Semaphoreを取得しようとした場合、二番目のタスクは待機リスト(wait list)に付け加えられます。待機リストの先頭にあるタスクはSemaphoreオブジェクトを持っているタスクが手放す時にSemaphoreを取得します。

acquireメソッドはパラメータとして状態を持っています。その理由は、acquireメソッドがSemaphoreオブジェクトの待機リストにそのタスクを付け加えるからです。そのメソッドはもしsemaphoreを取得した場合、trueを返します。これは、そのタスクは続けることができ、semaphoreによって保護された資源を用いることができることを意味します。もし、メソッドがfalseを返した場合、そのタスクはexecuteメソッドから戻るべきです。acquireメソッドに対する状態パラメータはexecuteメソッドが次に呼ばれたときの状態になります。acquireメソッドはacquiredTask変数のなかでSemaphoreを取得したタスクのタスク参照を保存します。

割り込みの場合
そのタスクがInterruptTaskで割り込みが起こった場合、タスクのexecuteメソッドの次の状態値が割り込みの状態になります。セマフォのacquireメソッド呼び出しで示された状態は次のexecute呼び出しになります。同様に、割り込みの状態はセマフォが取得されるまで入りません。


releaseメソッドはSemaphoreオブジェクトに関連付けられた資源とともに完了したときにのみ呼ばれるべきです。EventクラスがSemaphoreクラスのスーパークラスですからnotifyメソッドを含んでいます。causeメソッドはセマフォを開放しますから、プログラムはセマフォが取得された後で起こる設定のみを行うべきです。

acquireメソッドはSemaphoreオブジェクトがすでに取得されているときに待機リストにタスクを付け加えます。これは必ずしも望まれたことではありません。例えば、利用できるようになったときにタスクがほかの事をしたり、コントロールされた資源が必要になったりするからです。この場合、readyメソッドはSemaphoreオブジェクトのステータスを問い合わせすることができます。もしオブジェクトが取得できた場合はtrueを返します。このタイプの手続きはマルチタスクシステムがノンプリエンプティブだから動作します。readyメソッドが呼ばれる時点と続く起こる取得したメソッド呼び出しの間でタスクスイッチがおこるため、もしこれがプリエンプティブなマルチタスクだとしたら動作しません。

SemaphoreクラスはsemaphoreListクラス変数とnextSemaphoreオブジェクト変数を用いてSemaphoreオブジェクトのリストを保存します。そのリストはデバッグの目的のみで用いられます。一般的な運用には要求されません。

どのようにしてSemaphoreオブジェクトが使われるかを示すサンプルアプリケーションを次に示します。

Maltitasking Test 5 - セマフォのデモ



MultitaskingTest5
#include "Semaphore.h"
#include "common.h"

/**
 * Multitasking Test 5 - セマフォのデモ
 */

class MultitaskingTest5 : public Task
{
private:
  static const int state1 = 1;
  static const int state2 = 2;
  static const int state3 = 3;

public:
  static Semaphore* semaphore ;
  char* name ;
  MultitaskingTest5 ( char* name );
  void show ( char* text );

protected:
   void execute ();
};


MultitaskingTest5.cpp
#include "MultitaskingTest5.h"

/**
 * Multitasking Test 5 - セマフォのデモ
 */

MultitaskingTest5::MultitaskingTest5 ( char* name )
{
  this->name = name ;
  state = 0;
  semaphore = new Semaphore () ;
}

void MultitaskingTest5::show ( char* text )
{
  pc.printf ( "%s", name ) ;
  pc.printf ( " : " ) ;
  pc.printf ( "%s\n", text ) ;
}

void MultitaskingTest5::execute () 
{
  switch ( state ) {
    case initialState:
      show ( "************* Initial state - acquire semaphore" ) ;
      semaphore->acquire ( state1 ) ;
      break ;

    case state1:
      show ( "************* State 1 - semaphore acquired" ) ;
      sleep(state2,1000) ;      // 待機(wait)して状態2に行く
      break ;

    case state2:
      show ( "************* State 2 - done sleeping" ) ;
      show ( "Releasing semaphore" ) ;
      semaphore->release () ;
      stop () ;
      break ;

    default:    // 異常の状態を検知した場合終了する
      stop () ;
      break;
  }
}

//  static
Semaphore* MultitaskingTest5::semaphore ;


メインプログラム



main.cpp
#include "mbed.h"
#include "MultitaskingTest5.h"

Serial pc(USBTX, USBRX); // tx, rx

int main()
{
  pc.baud(115200);
  pc.printf("MutlitaskingTest5\n");

  new MultitaskingTest5 ( "Task 1" ) ;
  new MultitaskingTest5 ( "Task 2" ) ;
  new MultitaskingTest5 ( "Task 3" ) ;

  // 各々のタスクはセマフォを取得を試みる
  // タスクは少しの期間待機する
  // そして、セマフォを解放し終了する。
  Task::TaskManager () ;
  pc.printf( "All done\n" ) ;
}

そのタスクはセマフォにアクセスを試みそれを解放します。Semaphoreオブジェクトを解放した後でタスクを終了します。結局は全てのタスクは戻ります。

Semaphoreオブジェクトは分離に用いられることができる、あるいはSemaphoreによって制御されるオブジェクトのクラスに対してスーパークラスのように用いることができます。どちらのアプローチも正しいが、後者は他の、多分より使える、スーパークラスを用いることをそのクラスから妨げるからさらに限定的な傾向になります。


実行結果
MutlitaskingTest5
Task 3 : ************* Initial state - acquire semaphore
Task 1 : ************* Initial state - acquire semaphore
Task 2 : ************* Initial state - acquire semaphore
Task 3 : ************* State 1 - semaphore acquired
Task 3 : ************* State 2 - done sleeping
Task 3 : Releasing semaphore
Task 1 : ************* State 1 - semaphore acquired
Task 1 : ************* State 2 - done sleeping
Task 1 : Releasing semaphore
Task 2 : ************* State 1 - semaphore acquired
Task 2 : ************* State 2 - done sleeping
Task 2 : Releasing semaphore
All done