OS_SetThreadDestructorStack

構文

#include <nitro/os.h>
void OS_SetThreadDestructorStack( void* stack );
  

引数

stack     スタックポインタの開始アドレス。
スタックは下位方向に積まれていくので、 スタックの最上位の位置を指定しなければならない点に注意してください。
アドレスは4バイトアラインメントされたものである必要があります。

返り値

なし。

説明

すべてのスレッドのデストラクタが実行される際のスタックを指定します。

stack で指定した値が、デストラクタ実行直前にスタックポインタとして代入されます。NULL を指定すると、この動作は行なわれません(デフォルトも同様です) 。スタックとして与える値は、領域の最上位アドレス(+1) となることに注意してください。

以下、さらに詳しい説明です。

あるスレッドが他のスレッドから OS_KillThread*() で破棄されてデストラクタが実行される場合、その時点での破棄されるスレッドのスタック消費量(あるいは残量)を正確に予測することは困難です。そのままスタックを使いつづけようとすると、たまたま大きくスタックを消費している瞬間にスレッドが破棄され、デストラクタを実行するにはスタックが足りないという事態に陥るかもしれません。

それを避けるために、OSThread システムでは、スレッドが別のスレッドから破棄された場合のスタックについて、

のいずれかの動作を選択出来るようになっています。本関数で NULL を指定するとデストラクタ実行直前にスタックポインタをスタック作成時の値まで戻すようになります。これはデフォルトの動作でもあります。NULL 以外のスタックを指定するとデストラクタ時にそれを使用するようになります。なお、複数のデストラクタが同時に実行されることはありませんので、すべてのスレッドに共通なスタックエリアを1つ指定して複数のデストラクタでスタックの使いまわしをすることに問題はありません。

デフォルトの動作に比べ、デストラクタ用のスタックを別に用意したほうがメモリの消費を押さえることが出来る場合もあります。例えば普段スタックを100バイトしか使わないスレッドが、デストラクタでスタックを1000バイト消費する場合、デフォルト動作のためには予めスタックを 1000 バイト用意しなければなりません。スレッドが複数あった場合、それぞれに同様のことが当てはまります。しかしデストラクタスタックを 1000 バイト指定しておけば、普段の処理のための スタックは 100 バイトで済みます。

また、別スタックを用意しておけば、破棄されるスレッドがその時実行していた関数のローカル変数を潰すことがありません。例えば OSAlarm はすべてのアラームがリストで接続されています。ローカルで作成した OSAlarm をデストラクタでキャンセルしたいという場合はデストラクタまでスタック領域を残しておかなければなりません。

なお、OS_KillThread*() で自分のスレッドを指定した場合は、デフォルト動作のスタックポインタの戻しは行ないません。自らスレッドの終了を宣言することからスタックの使用状況は推測可能であるからです。ただし、この場合でも本関数で NULL 以外のデストラクタ用スタックが与えてある場合は、そちらをデストラクタ用のスタックとして使用するのはすべての場合で同様です。


別スタックを用意しなければならない例

以下のプログラムでは 10 秒後に「10 seconds passed.」と表示されるスレッドを main() 内で作っています。10 秒をカウントするのは OSAlarm システムを利用しています。10 秒経つまえにスレッドが破棄された場合は、スレッドデストラクタでアラームを削除しようとしています。しかし、アラーム構造体の本体はローカルな変数であるため、スレッドのスタックに存在しています。従ってデストラクタが呼ばれるときに元のスタックが破壊されることを避けなければなりません。 そこで OS_InitThread() の直後、 OS_SetThreadDestructorStack() でデストラクタ実行用のスレッドを指定しています。(ソース中、宣言など簡略化したり省略している個所があります)

OSAlarm* myAlarm = NULL;
u32 stack[ STACKSIZE ] ATTRIBUTE_ALIGN(32);

main()
{
    :
  OS_InitThread();
  OS_SetThreadDestructorStack( &stack[STACKSIZE] );
    :
  OSThread thread;
  OS_CreateThread( &thread, function, … );  // スレッドを作る
  OS_SetThreadDestructor( &thread, dtor );  // デストラクタを設定
  OS_WakeupThreadDirect( thread );       // スレッドを起す
    :
    :
  OS_KillThread( &thread );           // thread を破棄
    :

}

function()
{
  OSAlarm alarm;                  // alarm はローカル変数
  myAlarm = &alarm;
  OS_SetAlarm( &alarm, OSSecondsToTicks( 10 ), handler, … ); // 10秒後に handler が呼ばれる
    :
}

handler()
{
  OS_Printf( "10 seconds passed.\n" );     // 10秒たったと表示
  myAlarm = NULL;
}

dtor()
{
    :
  if ( myAlarm != NULL )
  {
    OS_CancelAlarm( myAlarm );          // アラームキャンセル
  }
    :
}

参照

OS_InitThread, OS_KillThread, OS_SetThreadDestructor

履歴

2005/08/08 初版