マルチタスク6

ShowStatusクラス タスクステータスとガーベージコレクション無しでの動作

この時点までに提示したマルチタスクのプログラムは比較的シンプルでテストされています。タスクの数が増えたりタスクがより複雑になってデバッグがより複雑になります。幸運にもデバッグが楽になる方法があります。

この時点で、mbedを用いてメモリー割り当てを簡単に見るのが便利です。特にC++(mbed)はガーベージコレクションをサポートしていません。これはそのアプリケーションを終了させてしまうほど全てのメモリーを割り付けてしまうことが可能ですから、もしアプリケーションが適切に設計されていない場合に問題になります。もしmbedがPCにつながっているのなら、mbedはエラーを通知するでしょう。

何かが起こる前に問題を捕捉することが好ましいでしょう。マルチタスクシステムの動向をつかむ、いくつかのステータスクラスとタスクを提示します。それらはどんな組み合わせのタスクでも用いることができます。今回の実験では何処でも用いていませんが、新しいアプリケーションを作る時や修正する時に問題に遭遇した場合に付け加えるべきです。

最初のステップはShowStatusクラスです。これはTaskStatusSemaphoreStatusクラスの基礎になります。次のソースがShowStatusクラスです。

ShowStatus.h
#ifndef _SHOWSTATUS
#define _SHOWSTATUS

#include "common.h"
#include "Event.h"
/**
 * マルチタスクのステータスをサポートするクラス
 * このクラスはオブジェクトのみ
 */

class ShowStatus {
  protected:
   static void print ( Event* event ) {
    pc.printf ( "<%s>", event->name ()) ;
  }

  static void print ( int value ) {
    pc.printf ( "%d", value ) ;
  }

  static void print ( char* string ) {
    pc.printf ( "%s", string ) ;
  }

  static void println ( char* string ) {
    pc.printf ( "%s\n",  string ) ;
  }

  static void println () {
    pc.printf ( "\n" ) ;
  }
};

#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 


このクラスは、TaskStatusSemaphoreStatusクラスのようにクラスメソッドのみでできています。コンストラクタは何もしないクラスオブジェクトを作るのを防ぐように保護されています。このクラスはstream変数を変えることによってリダイレクトされる基本的なprintをサポートしています。EventクラスはTaskSemaphoreクラスね基本であることを注意してください。

次はTaskStatusクラスです。

TaskStatusクラス TaskStatus.h



TaskStatus.h
#ifndef TASKSTATUS
#define TASKSTATUS

#include "Task.h"
#include "ShowStatus.h"
#include "common.h"

/**
 * マルチタスクのステータスを表示する
 *
 * アクティブタスクとフリーメモリを表示する
 * これはクラスオブジェクトのみ
 */

class TaskStatus : public ShowStatus
{
private:
  static const int taskRunning = 0;
  static const int taskSuspended = 1;
  static const int taskSemaphoreWait = 2;

protected:
  TaskStatus () {
    // オブジェクトの生成を防ぐ
  }

  static void printTaskStatus ( int taskStatus );
  static void printStateName ( int state );
public:
  /**
   * メモリーのステータスを書き出す
   */
  static void printMemoryStatus ();
  /**
   * タスクのステータスを書き出す
   */
  static void printTaskStatus ( Task* task );
  /**
   * タスクのリストを書き出す
   *
   * 入力:String prefix: タスクのリストの前に書く
   * 入力:boolean running; もし走っているタスクをリストする場合にtrue
   */
  static void printTasks ( String prefix, bool running );
  static void show ();
}; // class

#endif


TaskStatusクラス TaskStatus.cpp



TaskStatus.cpp
#include "TaskStatus.h"

void TaskStatus::printTaskStatus ( int taskStatus )
{
  switch ( taskStatus )
  {
    case taskRunning:
      print ( "Running   " ) ;
      break ;

    case taskSuspended:
      print ( "Suspended " ) ;
      break ;

    case taskSemaphoreWait:
      print ( "Semaphore " ) ;
      break ;

    default:
      print ( "-- Unknown status -- " ) ;
      break ;
  }
}

void TaskStatus::printStateName ( int state )
{
  switch ( state )
  {
    case Task::initialState:
      print ( "initial " ) ;
      break ;

    case Task::checkTimer:
      print ( "sleeping" ) ;
      break ;

    case Task::interrupt:
      print ( "interrupt" ) ;
      break ;

    case Task::terminate:
      print ( "terminate" ) ;
      break ;

    case Task::stopped:
      print ( "stopped  " ) ;
      break ;

    default:
      print ( state ) ;
      break ;
  }
}

/**
 * メモリーのステータスを書き出す
 */

void TaskStatus::printMemoryStatus ()
{
  int* h=(int*)malloc(0);
  int latestFreeMemory=(int)(&h)-(int)h;
  print   ( latestFreeMemory) ;
  println ( " bytes free" ) ;
}

/**
 * タスクのステータスを書き出す
 */
void TaskStatus::printTaskStatus ( Task* task ) {
  print   ( task ) ;
  printTaskStatus ( task->__taskStatus ) ;
  printStateName ( task->state ) ;
  println () ;
}

/**
 * タスクのリストを書き出す
 *
 * 入力:String prefix: タスクのリストの前に書く
 * 入力:boolean running; もし走っているタスクをリストする場合にtrue
 */

void TaskStatus::printTasks ( String prefix, bool running )
{
  println ( prefix ) ;
  for ( Task* task = Task::taskStatusList
      ; task != (Task*)null
      ; task = task->nextTaskStatus
      ) {
    if ( task->taskStatus() == 0) {
      print ( " " ) ;
      printTaskStatus ( task ) ;
    }
  }
}

void TaskStatus::show ()
{
  Task* task = Task::taskList ;

  println ( "== Task Status ==" ) ;
  printMemoryStatus () ;

  printTasks ( "Tasks running", true ) ;
  printTasks ( "Tasks not running", false ) ;

  if ( task == (Task*)null ) {
    println ( "-- Empty task list --" ) ;
  } else {
    // Check taskList integrity
    do {
      task = task->nextTask ;
      if ( task == (Task*)null ) {
        println ( "== Error: Task list not circular ==" ) ;
        break ;
      }
    } while ( Task::taskList != task ) ;
  }
  println () ;
} // show


showメソッドは全てのタスクの詳細な説明をします。個々のタスクステータスはprintTaskStatusを用いて得ることができます。このサポートはTaskクラスの代わりにこのクラスに含まれているので、もしステータスメソッドが使われなければTaskを用いてオーバーヘッドが付加されません。

SemaphoreStatusクラスはTaskStatusクラスに非常に似ています。

SemaphoreStatusクラス SemaphoreStatus.h



SemaphoreStatus.h
#ifndef _SEMAPHORESTATUS
#define _SEMAPHORESTATUS

#include "ShowStatus.h"
#include "TaskStatus.h"
#include "common.h"

#include "Semaphore.h"
/**
 * マルチタスクのステータスを表示する
 *
 * アクティブタスクとフリーメモリを表示する
 * これはクラスオブジェクトのみ
 */

class SemaphoreStatus : public ShowStatus 
{
protected:
  SemaphoreStatus (){};

public:
  /**
   * セマフォ情報を含んだタスクステータスを書き出す
   */
  static void printTaskStatus ( Task* task );
  static void show ();
};
#endif


SemaphoreStatusクラス SemaphoreStatus.cpp



SemaphoreStatus.cpp
#include "SemaphoreStatus.h"
/**
 * マルチタスクのステータスを表示する
 *
 * アクティブタスクとフリーメモリを表示する
 * これはクラスオブジェクトのみ
 */

/**
 * セマフォ情報を含んだタスクステータスを書き出す
 */
void SemaphoreStatus::printTaskStatus ( Task* task ) 
{
  // 一般的なステータスを書き出す
  TaskStatus::printTaskStatus ( task ) ;

  // 取得したセマフォを書き出す
  for ( Semaphore* semaphore = Semaphore::semaphoreList
      ; semaphore != (Semaphore*)null
      ; semaphore = semaphore->nextSemaphore
      ) {
    if ( semaphore->acquiredTask == task ) {
      print ( " Acquired " ) ;
      print ( semaphore->name() ) ;
      println () ;
    }
  }

  // セマフォ待ちステータスを書き出す
  for ( Semaphore* semaphore = Semaphore::semaphoreList
      ; semaphore != (Semaphore*)null
      ; semaphore = semaphore->nextSemaphore
      ) {
    for ( Task* waitingTask = semaphore->waitingTaskList
        ; waitingTask != (Task*)null
        ; waitingTask = waitingTask->nextTask
        ) {
      if ( waitingTask == task ) {
        print ( " Waiting for " ) ;
        print ( semaphore->name() ) ;
        println () ;
      }
    }
  }
}

/**
 * 
 */
void SemaphoreStatus::show () {
  println ( "== Semaphore Status ==" ) ;

  for ( Semaphore* semaphore = Semaphore::semaphoreList
      ; semaphore != (Semaphore*)null
      ; semaphore = semaphore->nextSemaphore
      ) {
    print ( semaphore->name() ) ;
    if ( semaphore->ready ()) {
      println ( " ready" ) ;
    } else {
      print ( " acquired by " ) ;
      print ( semaphore->acquiredTask) ;
      println () ;

      if ( (semaphore->acquiredTask)->state == Task::stopped) {
        println ( " ** Error: Task stopped **" ) ;
      }

      println ( " Waiting tasks" ) ;

      int i=0;
      for ( Task* task = semaphore->waitingTaskList
          ; task != (Task*)null
          ; task = task->nextTask
          , i++
          ) {
        print ( "  " ) ;
        print ( task ) ;
        println () ;
        if(i>=10) {
           println("Exit over");
           break;
        }
      } // for
    } // if
  } // for
  println () ;
}

showメソッドはTaskStatusクラスの中のshowメソッドと似た動作をします。そのメソッドは、タスクに含まれている詳細を与える各々のセマフォのsemaphoreListwaitingTaskListを歩き回ります。printTaskStatusメソッドは同じ名前のTaskStatusと比べてより詳細な報告をします。両方のshowメソッドは通常一緒に呼ばれますが、SemaphoreStatus.printTaskStatusTaskStatusクラスで与えられる情報のスーパーセット(上位集合)なので単独で通常使われます。一般的にもしセマフォがアプリケーションによって用いられないならばSemaphoreStatusクラスは必要がありません。


WatchHeapTaskクラス WatchHeapTask.h

次はWatchHeapTaskです。このタスクはメモリー利用と変化の報告を追跡するように設計されています。次のソースがこのクラス定義です。

WatchHeapTask.h
#ifndef _WATCHHEAPTASK
#define _WATCHHEAPTASK

#include "Task.h"
#include "common.h"
#include 
/**
 * ヒープ空間を監視するクラス
 *
 * 全てのメモリーが割り当てられた後でこのタスクを開始する
 * フリーメモリーを追跡し、変化を報告する。
 */
#include "mbed.h"

class WatchHeapTask : public Task
{
public:
  Event*   event ;

/**
 * WatchHeapTaskを作る
 * エラーを報告する
 */
WatchHeapTask () {
}


/**
 * Control error checking
 */
void enable ( bool enableNow );
/**
 * タスク名を返す
 */
static String name ();
/**
 * WatchHeapTaskを作る
 * イベントを通知することによりエラーを報告する
 *
 * 入力け:Event event:ヒープが成長したときに通知するイベント
 */
WatchHeapTask ( Event* event );
protected:
  int     freeMemory;
  bool _enable ;
  void execute ();
};

#endif


WatchHeapTaskクラス WatchHeapTask.cpp



WatchHeapTask.cpp
#include "WatchHeapTask.h"

void WatchHeapTask::enable ( bool enableNow ) 
{
  _enable = enableNow ;
  if ( enableNow ) {
    freeMemory = 0 ;
  }
}
/**
 * タスク名を返す
 */
String WatchHeapTask::name () {
  return "WatchHeap" ;
}

/**
 * WatchHeapTaskを作る
 * イベントを通知することによりエラーを報告する
 *
 * 入力け:Event event:ヒープが成長したときに通知するイベント
 */
WatchHeapTask::WatchHeapTask ( Event* event ) {
  this->event = event ;
  freeMemory=0;
  _enable = true ;
}

void WatchHeapTask::execute () {
/* Memory.freeMemoryはスタック位置依存である
 * 一貫性のある情報を与えるために同じ位置で呼ばれる必要がある
 */
  int* h=(int*)malloc(0);
  int latestFreeMemory=(int)(h) - (int)&h;
  // このタスクだけが走っているならば終了する
  if ( nextTask == this ) {
    stop () ;
    return ;
  }

  if ( _enable ) {
    if ( freeMemory == 0 ) {
      freeMemory = latestFreeMemory ;
    } else if ( freeMemory != latestFreeMemory ) {
      if ( event == nullEvent ) {
        pc.printf( "Error: Memory leak \n" ) ;
        pc.printf   ( " Last: %d\n", freeMemory ) ;
        pc.printf  ( " Now:  %d\n",latestFreeMemory ) ;
      } else {
        event->notify () ;
      }
      // Reset low water mark
      freeMemory = latestFreeMemory ;
    }
  }
}


メモリーチェックはenableメソッドでenableにしたりdisableにしたりできます。チェックすることを可能にすることはfreeMemory変数をリセットして、そのタスク実行を次に設定します。これはそのタスクを一時停止するのと異なることに注意してください。後者の場合、freeMemory変数はリセットされますが、タスクはそのタスクが再開されるまでどんな変化でも検出することができません。

この全ての新しく見つけた情報で何ができるでしょうか。元のマルチタスクのデモプログラムに戻って見ると次のようになります。

MutlitaskingTest6クラス MutlitaskingTest6.h



MutlitaskingTest6.h
#include "Task.h"
#include "common.h"
#include "Semaphore.h"

/**
 * MutlitaskingTest6
 */

class MultitaskingTest6 : public Task
{
public:
  const static int State1 = 1;
  const static int State2 = 2 ;
  const static int State3 = 3 ;

  static Semaphore* _semaphore;

  MultitaskingTest6 ( String name );
  String name ();
  void show ( String text );
  void execute ();
}; // class



MultitaskingTest6クラス MultitaskingTest6.cpp



MultitaskingTest6.cpp
#include "MultitaskingTest6.h"
Semaphore* MultitaskingTest6::_semaphore;

MultitaskingTest6::MultitaskingTest6 ( String name )
{
  state=0;
  Name = name ;
  _semaphore = new Semaphore () ;
  pc.printf("MutlitaskingTest6\n");
}

/**
 * タスク名を得る
 *
 * 戻り: タスク名
 */
String MultitaskingTest6::name ()
{
  return Name ;
}

void MultitaskingTest6::show ( String text )
{
  pc.printf ( "%s: %s\n", Name , text) ;
}

void MultitaskingTest6::execute ()
{
  switch ( state ) {
    case initialState:
      show ( "Initial state" ) ;
      nextState ( State1 ) ;
      break ;

    case State1:
      show ( "State 1" ) ;
       nextState ( State2 ) ;
      break ;

    case State2:
      show ( "State 2" ) ;
      if ( ! _semaphore->acquire ( State3 )) {
        stop () ;
      }
      break ;

    case State3:
      show ( "State 3" ) ;
    default:    // おかしな状態を捕獲した場合はデフォールトで終了
      stop () ;
      break;
  } //switch
} // execute



MutitaskingTest6オブジェクトは全部それに付けられている唯一の名前を持っています。これはもしクラスのただ1つのインスタンスが作られるならば、クラス定義で度々行われます。この場合、3つの異なったタスクがデバッグに利用できます。ただ1つのSemaphoreがありその名前はデフォールトとして残してあります。

TaskStatusSemaphoreStatusshowメソッドはタスクマネージャが走っているプログラムの開始する前後の一般的な概観を見るのに用いられます。これらのメソッドはシステムが同様に走っている間呼ばれることができます。個々のタスクステータスの情報はprintTaskStatusメソッドを用いて終わりに表示されます。

WatchHeapTaskオブジェクトは同様に作られます。Integerオブジェクトとして割り付けることによってMultitaskingTest6のstate1でメモリーリークをしています。これはフリースペースの総量を減らすからどんなJavaのオブジェクトでもできます。

mainメソッドを走らせることはメッセージウィンドウの中に短いテキストを表示します。初期のスタータス情報は走っている4つのタスクを表示します。全ての3つのタスクはほとんど同時にメモリーを割り付けるからただ一度だけですが、WatchHeapTaskはメモリーリークも報告します。WatchHeapTaskタスクは1つの増加に気付くだけですが、その問題をあなたに通知します。

終わりでの情報はexecuteメソッドの中でプログラムされた2つのエラーを表示します。最初のは孤立したセマフォを取得した間task1が停止したことです。2番目の問題は他の2つのタスクが決して解放されないこのセマフォで待機になっていることです。これは他の2つのタスクが決して続けられないことを意味しています。

MultitaskingTest6クラスは2つの共通のプログラムエラーといかに検出したかを表示します。そのステータスクラスは同様に表示されます。それらはマルチタスクのアプリケーションの実行の中に考慮されます。

実行結果
MutlitaskingTest6実行結果
MutlitaskingTest6
task1 = 10000AA0
MutlitaskingTest6
task2 = 10000B00
MutlitaskingTest6
task3 = 10000B60
一般的なステータスを表示
== Task Status ==
29604 bytes free
Tasks running
 <Task>Running   initial
 <Task 3>Running   initial
 <Task 2>Running   initial
 <Task 1>Running   initial
Tasks not running
 <Task>Running   initial
 <Task 3>Running   initial
 <Task 2>Running   initial
 <Task 1>Running   initial

== Semaphore Status ==
Semaphore ready
Semaphore ready
Semaphore ready

タスクを走らせる
Task 1: Initial state
Task 2: Initial state
Task 3: Initial state
Task 1: State 1
Task 2: State 1
Task 3: State 1
Task 1: State 2
Task 2: State 2
Task 3: State 2
一般的なステータスを表示
== Task Status ==
29524 bytes free
Tasks running
Tasks not running
-- Empty task list --

== Semaphore Status ==
Semaphore acquired by <Task 1>
 ** Error: Task stopped **
 Waiting tasks
  <Task 2>
  <Task 3>
Semaphore ready
Semaphore ready

ステータスを表示
<Task 1>Suspended stopped
 Acquired Semaphore
<Task 2>Semaphore 3
 Waiting for Semaphore
<Task 3>Semaphore 3
 Waiting for Semaphore
<Task>Suspended stopped
All done