/*
 *	cliprrs.c
 *
 *	Round Robin Scheduler (RRS)
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Sun Jan 17 21:27:14 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	* Tue Jan 19 21:44:45 JST 2016 Naoyuki Sawa
 *	- cliprrs.cの一番下に『注意事項』を追記しました。
 *	  プログラムの変更は有りません。
 *	* Fri Feb 19 21:59:00 JST 2016 Naoyuki Sawa
 *	- RRS_new_l(),RRS_new_a(),RRS_new_v()の引数を変更しました。
 *	  これまでは構造体のポインタを戻り値で返していましたが、変更後は第一引数で指定する変数に格納するようにしました。
 *	  変更した理由は、『注意事項』の2016/02/19の追記部分を参照して下さい。
 *	* Sun Feb 28 23:18:54 JST 2016 Naoyuki Sawa
 *	- RRS_n_job(),RRS_i_job()を追加しました。
 *	  アプリケーションはジョブの数を知っていますし、各ジョブの処理は自分自身を認識していますので、普通はこの関数を呼び出す必要は有りません。
 *	  今回当関数を追加した目的は、各ジョブから共通で呼び出されるログ処理で、何番目のジョブから呼び出されたかを記録する必要が生じたためです。
 *	  たとえば、以下のように記録する事が出来ます。
 *	  │syslog(LOG_DEBUG, "call from job#%d/%d", RRS_i_job()+1, RRS_n_job()); //何個中の何番目のジョブから呼ばれたかを記録する。
 *	* Wed Jun 15 21:27:07 JST 2016 Naoyuki Sawa
 *	- 「ジョブ同期用の関数」として、RRS_sync()を追加しました。
 *	  使用例は、cliprrs.cの「ジョブ同期用の関数の使用例」を参照して下さい。
 *	- RRS_sync()を追加した理由は、pthread_barrier_wait()と同様の制御が行いたかったからです。
 *	  既存のclipmsgh.cモジュールのメッセージハンドシェイク機能を使って同期を実現する事も不可能ではないのですが、送受信のフェーズが複雑になってバグを生じ易い問題が有ります。
 *	  特に、メッセージハンドシェイク機能を使って三個以上のジョブを同期する事は、非常に困難だと思います。
 *	  そこで、ジョブ同期用に特化した機能として、RRS_sync()を追加する事にしました。
 *	  RRS_sync()を使えば、三個以上のジョブを同期する事も容易ですし、二個のジョブを同期する処理も、メッセージハンドシェイク機能を使うよりもシンプルに書けます。
 *	  具体例は、cliprrs.cの「ジョブ同期用の関数の使用例」を参照して下さい。
 *	* Thu Jun 16 20:40:56 JST 2016 Naoyuki Sawa
 *	- 'バリアが解除された事を示すフラグがセットされるまでループして待つ'処理の中で、ノードチェインが破壊されていないかを検査するデバッグ処理を追加しました。
 *	  詳細は、RRS_sync()内の'Thu Jun 16 20:40:56 JST 2016'のコメントを参照して下さい。
 *	* Fri Sep 30 21:41:38 JST 2016 Naoyuki Sawa
 *	- RRS_new_v(),RRS_yield()の、Hew用の実装を作成しました。
 *	  「/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z」の、2016/09/30バージョンを利用しています。
 *	  動的メモリ割り当てを避けるために、スタックバッファサイズは固定としました。
 *	- RRS_sync_idle()を追加しました。
 *	  これまで、RRS_sync()内でブロックされている間は、アプリケーション定義の周期処理が呼び出せない問題が有りました。
 *	  その問題を解決するために、ブロックされている間に呼び出されるアプリケーション定義のアイドリング関数を指定出来るバージョンの関数を追加しました。
 *	* Sat Oct 01 21:11:57 JST 2016 Naoyuki Sawa
 *	- RRS_new_v(),RRS_yield()のHew用の実装で、Context.srcをバイナリ化してC言語ソースに埋め込みました。
 *	  もう、Context.hとContext.srcを、プロジェクトに含める必要は無くなりました。
 *	  「/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z」の、2016/10/01バージョンを利用しています。
 *	  詳細は、/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z内のREADME.TXTの、同日のコメントを参照して下さい。
 *	* Sat Dec 03 21:41:01 JST 2016 Naoyuki Sawa
 *	- プラットフォーム依存部分の実装方法を選択出来るようにしました。
 *	  これまでは、Windows環境ではPthreads、P/ECE環境ではucontextに決め打ちにしていたのですが、他の実装方法も選択出来るようになりました。
 *	- これまで、Windows環境でPthreads決め打ちにしていた理由は、Boehm GCとucontext,及び,Boehm GCとFiberを併用出来なかったからです。
 *	  しかし、2016/12/01に、pcl.cモジュールにて、Boehm GCとコルーチンを併用する方法が分かり、同じ方法が当モジュールにも適用出来る事に気付きました。
 *	  当モジュールでも、Windows環境でのucontextとFiber使用時に、pcl.cモジュールと同じ方法を適用したので、Windows環境でもucontextとFiberが使えるようになりました。
 *	- Pthreadsよりも、ucontext(又はFiber)を使う事の利点は、コンテキストスイッチの処理が早い事です。
 *	  大まかに計測してみたところ、Pthreadsよりもucontextの方が、100倍以上早いようです。
 *	  とは言え、Pthreadsによるコンテキストスイッチでも、数マイクロ秒程度しかかかっていないようなので、Pthreadsでも問題は無いです。
 *	- _beginthreadexによる実装と、PCLによる実装を追加しました。
 *	  _beginthreadexによる実装を追加した理由は、'POSIX Threads Library for Win32'ライブラリが無くても、本物のスレッドによる実装が可能である事を検証するためです。
 *	  もし今後、'POSIX Threads Library for Win32'ライブラリが利用出来なくなった場合のための保険であり、現在の所はあまり重要ではありません。
 *	  PCLによる実装を追加した理由は、pcl.cモジュールの動作検証の一つとして実装しただけであり、これも現在の所はあまり重要ではありません。
 */
#include "clip.h"
/****************************************************************************
 *	プラットフォーム依存部分の実装方法を選択する。
 ****************************************************************************/
//┌──────────────┬───┬───┬───┬───────────────────────────────────────┐
//│				│Win32	│P/ECE	│Hew	│Windows環境においてBoehm GCと併用するための対策				│コンテキストスイッチ時間(Windows10 64bit Core i7-4500Uで計測)
//├──────────────┼───┼───┼───┼───────────────────────────────────────┤
//│Pthreadsによる実装		│〇	│×	│×	│本物のスレッドであり、Boehm GCが正しく認識出来るので、併用の対策は不要です。	│4146.500000[ナノ秒/回]
//│_beginthreadexによる実装	│〇	│×	│×	│本物のスレッドであり、Boehm GCが正しく認識出来るので、併用の対策は不要です。	│4218.750000[ナノ秒/回]
//│ucontextによる実装		│〇	│〇	│×	│当モジュールのRRS_yield()内で行っています。					│  29.250000[ナノ秒/回]
//│Fiberによる実装		│〇	│〇	│×	│当モジュールのRRS_yield()内で行っています。					│  31.250000[ナノ秒/回]
//│PCLによる実装		│〇	│〇	│×	│pcl.cモジュールのco_call()内で行っているので、当モジュールでは対策不要です。	│  50.750000[ナノ秒/回]
//│RXマイコンによる実装	│×	│×	│〇	│										│
//└──────────────┴───┴───┴───┴───────────────────────────────────────┘
#ifdef  WIN32
//{{どれか一つを選択して下さい。
//	#define RRS_USE_PTHREADS	//Pthreadsによる実装		2016/12/02まではこれを使用していました。実際の応用で、安定して動作していました。
//	#define RRS_USE_BEGINTHREADEX	//_beginthreadexによる実装	軽く動作確認した限りでは正しく動作しました。
	#define RRS_USE_UCONTEXT	//ucontextによる実装		2016/12/03以降はこれを使用します。安定して動作するか、引き続き、動作検証中です。
//	#define RRS_USE_FIBER		//Fiberによる実装		軽く動作確認した限りでは正しく動作しました。
//	#define RRS_USE_PCL		//PCLによる実装			軽く動作確認した限りでは正しく動作しました。
//}}どれか一つを選択して下さい。
#endif//WIN32
/*--------------------------------------------------------------------------*/
#ifdef  PIECE
//{{どれか一つを選択して下さい。
	#define RRS_USE_UCONTEXT	//ucontextによる実装		2016/12/02までも、2016/12/03以降も、これを使用します。引き続き、動作検証中です。
//	#define RRS_USE_FIBER		//Fiberによる実装		軽く動作確認した限りでは正しく動作しました。
//	#define RRS_USE_PCL		//PCLによる実装			軽く動作確認した限りでは正しく動作しました。
//}}どれか一つを選択して下さい。
#endif//PIECE
/*--------------------------------------------------------------------------*/
#ifdef  __RENESAS__
//{{どれか一つを選択して下さい。
	#define RRS_USE_RXMCU		//RXマイコンによる実装		2016/12/02までも、2016/12/03以降も、これを使用します。安定して動作していました。
//}}どれか一つを選択して下さい。
#endif//__RENESAS__
/****************************************************************************
 *	プラットフォーム依存部分 - Pthreadsによる実装
 ****************************************************************************/
#ifdef  RRS_USE_PTHREADS
/*typedef*/ struct _ST_RRS {
	uint16_t	n_job;			//ジョブの数					   =RRS_new_*()に指定した関数の数+1/*RRS_new_*()を呼び出したコンテキストのジョブ
	uint16_t	i_job;			//現在実行中のジョブの順番			 0 =RRS_new_*()に指定した1番目の関数のジョブ      , 1 =RRS_new_*()に指定した2番目の関数のジョブ      ,…, n_job-1 = RRS_new_*()を呼び出したコンテキストのジョブ
	sem_t		TBL_sem[0/*n_job*/];	//各ジョブに対応するセマフォ			[0]=RRS_new_*()に指定した1番目の関数のジョブに対応,[1]=RRS_new_*()に指定した2番目の関数のジョブに対応,…,[n_job-1]= RRS_new_*()を呼び出したコンテキストのジョブに対応
} /*ST_RRS*/;
/*--------------------------------------------------------------------------*/
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/) {
	//関数の数(=自分以外のジョブの数)を数える。
	int i = 0;
	while(aFn[i]) { i++; }
//不要	if(!i) { DIE(); }	//関数の数が0個でも(無意味だが)動作上は問題無いので、エラーにしない事にした。その場合、自分のジョブだけが走る事になる。
	//構造体のメモリを確保する。
	*ppRRS = calloc(sizeof(ST_RRS) + sizeof((*ppRRS)->TBL_sem[0]) * (i + 1/*自分*/), 1);
	if(!*ppRRS) { DIE(); }
	//ジョブの数を格納する。
	(*ppRRS)->n_job = i + 1/*自分*/;
	//セマフォを初期化する。
	for(i = 0; i < (*ppRRS)->n_job; i++) {
		if(sem_init(&(*ppRRS)->TBL_sem[i], 0, 0)) { DIE(); }
	}
	//自分以外の各ジョブについて…
	for(i = 0; i < (*ppRRS)->n_job - 1/*自分*/; i++) {
		pthread_t tid;
		pthread_attr_t attr;
		//スレッドを作成する。
		if(pthread_attr_init(&attr)) { DIE(); }
		if(stacksize) { if(pthread_attr_setstacksize(&attr, stacksize)) { DIE(); } }
		if(pthread_create(&tid, &attr, (void* (*)(void*))aFn[i], *ppRRS)) { DIE(); }
		if(pthread_attr_destroy(&attr)) { DIE(); }
		if(pthread_detach(tid)) { DIE(); }	//この後スレッド識別子を使わないのですぐデタッチして構わない。
		//このスレッドが初回のyieldを行うまで待つ。
		if(sem_wait(&(*ppRRS)->TBL_sem[i + 1])) { DIE(); }
	}
	//ここで自分の順番になっています。
}
void RRS_yield(ST_RRS* pRRS) {
	sem_t *p_sem1, *p_sem2;
	//自分のジョブのセマフォを取得する。
	p_sem1 = &pRRS->TBL_sem[pRRS->i_job];
	//順番を次へ進める。
	if(++pRRS->i_job == pRRS->n_job) { pRRS->i_job = 0; }
	//次のジョブのセマフォを取得する。
	p_sem2 = &pRRS->TBL_sem[pRRS->i_job];
	//次のジョブをアクティブにする。
	if(sem_post(p_sem2)) { DIE(); }
	//自分の順番が来るまで待つ。
	if(sem_wait(p_sem1)) { DIE(); }
}
#endif//RRS_USE_PTHREADS
/****************************************************************************
 *	プラットフォーム依存部分 - _beginthreadexによる実装
 ****************************************************************************/
#ifdef  RRS_USE_BEGINTHREADEX
/*typedef*/ struct _ST_RRS {
	uint16_t	n_job;			//ジョブの数					   =RRS_new_*()に指定した関数の数+1/*RRS_new_*()を呼び出したコンテキストのジョブ
	uint16_t	i_job;			//現在実行中のジョブの順番			 0 =RRS_new_*()に指定した1番目の関数のジョブ      , 1 =RRS_new_*()に指定した2番目の関数のジョブ      ,…, n_job-1 = RRS_new_*()を呼び出したコンテキストのジョブ
	HANDLE		TBL_evt[0/*n_job*/];	//各ジョブに対応するイベント			[0]=RRS_new_*()に指定した1番目の関数のジョブに対応,[1]=RRS_new_*()に指定した2番目の関数のジョブに対応,…,[n_job-1]= RRS_new_*()を呼び出したコンテキストのジョブに対応
} /*ST_RRS*/;
/*--------------------------------------------------------------------------*/
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/) {
	//関数の数(=自分以外のジョブの数)を数える。
	int i = 0;
	while(aFn[i]) { i++; }
//不要	if(!i) { DIE(); }	//関数の数が0個でも(無意味だが)動作上は問題無いので、エラーにしない事にした。その場合、自分のジョブだけが走る事になる。
	//構造体のメモリを確保する。
	*ppRRS = calloc(sizeof(ST_RRS) + sizeof((*ppRRS)->TBL_evt[0]) * (i + 1/*自分*/), 1);
	if(!*ppRRS) { DIE(); }
	//ジョブの数を格納する。
	(*ppRRS)->n_job = i + 1/*自分*/;
	//イベントを作成する。
	for(i = 0; i < (*ppRRS)->n_job; i++) {
		(*ppRRS)->TBL_evt[i] = CreateEvent(NULL, FALSE/*自動リセットオブジェクト*/, FALSE/*非シグナル状態*/, NULL);
		if(!(*ppRRS)->TBL_evt[i]) { DIE(); }
	}
	//自分以外の各ジョブについて…
	for(i = 0; i < (*ppRRS)->n_job - 1/*自分*/; i++) {
		//スレッドを作成する。
		if(!_beginthreadex(NULL, stacksize, (unsigned (__stdcall *)(void*))aFn[i], *ppRRS, 0, NULL)) { DIE(); }	//厳密には__cdeclを__stdcallへキャストする事は安全ではない。しかしこの場合は引数が1個である事,及び,関数が処理を返さない事を前提としているので動作上問題無い。
		//このスレッドが初回のyieldを行うまで待つ。
		if(WaitForSingleObject((*ppRRS)->TBL_evt[i + 1], INFINITE) != WAIT_OBJECT_0) { DIE(); }
	}
	//ここで自分の順番になっています。
}
void RRS_yield(ST_RRS* pRRS) {
	HANDLE hEvent1, hEvent2;
	//自分のジョブのイベントを取得する。
	hEvent1 = pRRS->TBL_evt[pRRS->i_job];
	//順番を次へ進める。
	if(++pRRS->i_job == pRRS->n_job) { pRRS->i_job = 0; }
	//次のジョブのセマフォを取得する。
	hEvent2 = pRRS->TBL_evt[pRRS->i_job];
	//次のジョブをアクティブにする。
	if(!SetEvent(hEvent2)) { DIE(); }
	//自分の順番が来るまで待つ。
	if(WaitForSingleObject(hEvent1, INFINITE) != WAIT_OBJECT_0) { DIE(); }
}
#endif//RRS_USE_BEGINTHREADEX
/****************************************************************************
 *	プラットフォーム依存部分 - ucontextによる実装
 ****************************************************************************/
#ifdef  RRS_USE_UCONTEXT
/*typedef*/ struct _ST_RRS {
	uint16_t	n_job;			//ジョブの数					   =RRS_new_*()に指定した関数の数+1/*RRS_new_*()を呼び出したコンテキストのジョブ
	uint16_t	i_job;			//現在実行中のジョブの順番			 0 =RRS_new_*()に指定した1番目の関数のジョブ      , 1 =RRS_new_*()に指定した2番目の関数のジョブ      ,…, n_job-1 = RRS_new_*()を呼び出したコンテキストのジョブ
	ucontext_t	TBL_ucx[0/*n_job*/];	//各ジョブに対応するユーザーコンテキスト	[0]=RRS_new_*()に指定した1番目の関数のジョブに対応,[1]=RRS_new_*()に指定した2番目の関数のジョブに対応,…,[n_job-1]= RRS_new_*()を呼び出したコンテキストのジョブに対応
} /*ST_RRS*/;
/*--------------------------------------------------------------------------*/
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/) {
	//関数の数(=自分以外のジョブの数)を数える。
	int i = 0;
	while(aFn[i]) { i++; }
//不要	if(!i) { DIE(); }	//関数の数が0個でも(無意味だが)動作上は問題無いので、エラーにしない事にした。その場合、自分のジョブだけが走る事になる。
	//構造体のメモリを確保する。
	*ppRRS = calloc(sizeof(ST_RRS) + sizeof((*ppRRS)->TBL_ucx[0]) * (i + 1/*自分*/), 1);
	if(!*ppRRS) { DIE(); }
	//ジョブの数を格納する。
	(*ppRRS)->n_job = i + 1/*自分*/;
	//ユーザーコンテキストを作成する。	//自分のユーザーコンテキスト((*ppRRS)->TBL_ucx[(*ppRRS)->n_job-1])はまだ作成しない。
	if(!stacksize) {
#ifdef  WIN32	//Win32
		stacksize = 1048576/*調整可*/;	//既定値は1MBとした(2016/12/03現在)。理由はpcl.cのco_create()のコメントを参照せよ。
#else //WIN32	//P/ECE,Generic
		stacksize = 4096/*調整可*/;	//既定値は4KBとした(2016/12/03現在)。理由はpcl.cのco_create()のコメントを参照せよ。
#endif//WIN32
	}
	for(i = 0; i < (*ppRRS)->n_job - 1/*自分*/; i++) {
		ucontext_t* p_ucx = &(*ppRRS)->TBL_ucx[i];
		getcontext(p_ucx);
		p_ucx->uc_stack.ss_size = stacksize;
		p_ucx->uc_stack.ss_sp   = malloc(stacksize);
		if(!p_ucx->uc_stack.ss_sp) { DIE(); }
		makecontext(p_ucx, aFn[i], 1, *ppRRS);
	}
	//最初のジョブをアクティブにする。
	(*ppRRS)->i_job = i/*=((*ppRRS)->n_job-1)*/;
	RRS_yield(*ppRRS);			//自分のユーザーコンテキスト((*ppRRS)->TBL_ucx[(*ppRRS)->n_job-1])はここで作成する。
	//ここで自分の順番になっています。
}
void RRS_yield(ST_RRS* pRRS) {
	ucontext_t* p_ucx1, *p_ucx2;
#if     (defined(WIN32) && defined(GC_H))
	//切り替え前のジョブが、最後のジョブ(=RRS_new_*()を呼び出したコンテキストのジョブ)ならば…
	if(pRRS->i_job == (pRRS->n_job - 1)) {
		//'GC_dont_gc'をデクリメントする。
		GC_disable();
	}
#endif//(defined(WIN32) && defined(GC_H))
	//自分のジョブのユーザーコンテキストを取得する。
	p_ucx1 = &pRRS->TBL_ucx[pRRS->i_job];
	//順番を次へ進める。
	if(++pRRS->i_job == pRRS->n_job) { pRRS->i_job = 0; }
	//次のジョブのユーザーコンテキストを取得する。
	p_ucx2 = &pRRS->TBL_ucx[pRRS->i_job];
	//次のジョブをアクティブにする。
	swapcontext(p_ucx1, p_ucx2);
	//ここで自分の順番になっています。
#if     (defined(WIN32) && defined(GC_H))
	//切り戻し後のジョブが、最後のジョブ(=RRS_new_*()を呼び出したコンテキストのジョブ)ならば…
	if(pRRS->i_job == (pRRS->n_job - 1)) {
		//'GC_dont_gc'をデクリメントする。
		GC_enable();
		//最小限のガーベージコレクションを行う。
		GC_collect_a_little();
	}
#endif//(defined(WIN32) && defined(GC_H))
}
#endif//RRS_USE_UCONTEXT
/****************************************************************************
 *	プラットフォーム依存部分 - Fiberによる実装
 ****************************************************************************/
#ifdef  RRS_USE_FIBER
/*typedef*/ struct _ST_RRS {
	uint16_t	n_job;			//ジョブの数					   =RRS_new_*()に指定した関数の数+1/*RRS_new_*()を呼び出したコンテキストのジョブ
	uint16_t	i_job;			//現在実行中のジョブの順番			 0 =RRS_new_*()に指定した1番目の関数のジョブ      , 1 =RRS_new_*()に指定した2番目の関数のジョブ      ,…, n_job-1 = RRS_new_*()を呼び出したコンテキストのジョブ
	void*		TBL_fib[0/*n_job*/];	//各ジョブに対応するファイバー			[0]=RRS_new_*()に指定した1番目の関数のジョブに対応,[1]=RRS_new_*()に指定した2番目の関数のジョブに対応,…,[n_job-1]= RRS_new_*()を呼び出したコンテキストのジョブに対応
} /*ST_RRS*/;
/*--------------------------------------------------------------------------*/
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/) {
	//関数の数(=自分以外のジョブの数)を数える。
	int i = 0;
	while(aFn[i]) { i++; }
//不要	if(!i) { DIE(); }	//関数の数が0個でも(無意味だが)動作上は問題無いので、エラーにしない事にした。その場合、自分のジョブだけが走る事になる。
	//構造体のメモリを確保する。
	*ppRRS = calloc(sizeof(ST_RRS) + sizeof((*ppRRS)->TBL_fib[0]) * (i + 1/*自分*/), 1);
	if(!*ppRRS) { DIE(); }
	//ジョブの数を格納する。
	(*ppRRS)->n_job = i + 1/*自分*/;
	//ファイバーを作成する。
	// - 以下の処理で、ConvertThreadToFiber()を呼び出す前にCreateFiber()を呼び出していますが、この処理順序でも安全です。
	//   ConvertThreadToFiber()は、SwitchToFiber()よりも先に呼び出しておけば良く、CreateFiber()よりも先に呼び出す必要は無いからです。
	//   「MSDN - CreateFiber」(https://msdn.microsoft.com/ja-jp/library/cc429060.aspx)に、
	//   ＞スレッドがSwitchToFiber関数を使ってファイバをスケジューリングするためには、ConvertThreadToFiber関数を呼び出し、そのスレッドに関連付けられたファイバが存在するようにしておく必要があります。
	//   と書いてあり、(明記されてはいませんが)、CreateFiber()はConvertThreadToFiber()に依存していない事が読み取れます。
	// - Windows環境において同一スレッドがConvertThreadToFiber()を複数回呼び出すと失敗してNULLを返すようです。
	//   従ってアプリケーションが独自にConvertThreadToFiber()を使用している場合はRRSのFiberによる実装は使用出来ません。
	//   或いは、以下のように処理すれば回避できるかとも考えたのですが…
	//   │(*ppRRS)->TBL_fib[i/*=((*ppRRS)->n_job-1)*/] = GetCurrentFiber();		//既にアプリケーションがConvertThreadToFiber()を呼び出していると仮定してまず現在のファイバーを取得してみる。
	//   │if(!(*ppRRS)->TBL_fib[i/*=((*ppRRS)->n_job-1)*/]) {				//既にアプリケーションがConvertThreadToFiber()を呼び出していなければ…
	//   │  (*ppRRS)->TBL_fib[i/*=((*ppRRS)->n_job-1)*/] = ConvertThreadToFiber(*ppRRS);	//自分
	//   │  if(!(*ppRRS)->TBL_fib[i/*=((*ppRRS)->n_job-1)*/]) { DIE(); }
	//   │}
	//   …実際に試してみると、ConvertThreadToFiber()を呼び出す前にGetCurrentFiber()を呼び出すと、GetCurrentFiber()はNULLを返すのではなくデタラメな値を返すようで、上記の方法では回避出来ませんでした。
	//   やはり、アプリケーションが独自にConvertThreadToFiber()を使用している場合はRRSのFiberによる実装を使用する方法は無いようです。
	for(i = 0; i < (*ppRRS)->n_job - 1/*自分*/; i++) {
#ifdef  WIN32	//Win32
		(*ppRRS)->TBL_fib[i] = CreateFiber(stacksize, (void (__stdcall *)(void*))aFn[i], *ppRRS);	//厳密には__cdeclを__stdcallへキャストする事は安全ではない。しかしこの場合は引数が1個である事,及び,関数が処理を返さない事を前提としているので動作上問題無い。
#else //WIN32	//P/ECE,Generic
		(*ppRRS)->TBL_fib[i] = CreateFiber(stacksize, (void (          *)(void*))aFn[i], *ppRRS);
#endif//WIN32
		if(!(*ppRRS)->TBL_fib[i]) { DIE(); }
	}
	(*ppRRS)->TBL_fib[i/*=((*ppRRS)->n_job-1)*/] = ConvertThreadToFiber(*ppRRS);	//自分
	if(!(*ppRRS)->TBL_fib[i/*=((*ppRRS)->n_job-1)*/]) { DIE(); }	//当関数を呼びたす前に、アプリケーションがConvertThreadToFiber()を呼び出していた場合、Windows環境ではここでエラー停止します。
	//最初のジョブをアクティブにする。
	(*ppRRS)->i_job = i/*=((*ppRRS)->n_job-1)*/;
	RRS_yield(*ppRRS);
	//ここで自分の順番になっています。
}
void RRS_yield(ST_RRS* pRRS) {
#if     (defined(WIN32) && defined(GC_H))
	//切り替え前のジョブが、最後のジョブ(=RRS_new_*()を呼び出したコンテキストのジョブ)ならば…
	if(pRRS->i_job == (pRRS->n_job - 1)) {
		//'GC_dont_gc'をデクリメントする。
		GC_disable();
	}
#endif//(defined(WIN32) && defined(GC_H))
	//順番を次へ進める。
	if(++pRRS->i_job == pRRS->n_job) { pRRS->i_job = 0; }
	//次のジョブをアクティブにする。
	SwitchToFiber(pRRS->TBL_fib[pRRS->i_job]);
	//ここで自分の順番になっています。
#if     (defined(WIN32) && defined(GC_H))
	//切り戻し後のジョブが、最後のジョブ(=RRS_new_*()を呼び出したコンテキストのジョブ)ならば…
	if(pRRS->i_job == (pRRS->n_job - 1)) {
		//'GC_dont_gc'をデクリメントする。
		GC_enable();
		//最小限のガーベージコレクションを行う。
		GC_collect_a_little();
	}
#endif//(defined(WIN32) && defined(GC_H))
}
#endif//RRS_USE_FIBER
/****************************************************************************
 *	プラットフォーム依存部分 - PCLによる実装
 ****************************************************************************/
#ifdef  RRS_USE_PCL
/*typedef*/ struct _ST_RRS {
	uint16_t	n_job;			//ジョブの数					   =RRS_new_*()に指定した関数の数+1/*RRS_new_*()を呼び出したコンテキストのジョブ
	uint16_t	i_job;			//現在実行中のジョブの順番			 0 =RRS_new_*()に指定した1番目の関数のジョブ      , 1 =RRS_new_*()に指定した2番目の関数のジョブ      ,…, n_job-1 = RRS_new_*()を呼び出したコンテキストのジョブ
	coroutine_t	TBL_co[0/*n_job*/];	//各ジョブに対応するコルーチン			[0]=RRS_new_*()に指定した1番目の関数のジョブに対応,[1]=RRS_new_*()に指定した2番目の関数のジョブに対応,…,[n_job-1]= RRS_new_*()を呼び出したコンテキストのジョブに対応
} /*ST_RRS*/;
/*--------------------------------------------------------------------------*/
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/) {
	//関数の数(=自分以外のジョブの数)を数える。
	int i = 0;
	while(aFn[i]) { i++; }
//不要	if(!i) { DIE(); }	//関数の数が0個でも(無意味だが)動作上は問題無いので、エラーにしない事にした。その場合、自分のジョブだけが走る事になる。
	//構造体のメモリを確保する。
	*ppRRS = calloc(sizeof(ST_RRS) + sizeof((*ppRRS)->TBL_co[0]) * (i + 1/*自分*/), 1);
	if(!*ppRRS) { DIE(); }
	//ジョブの数を格納する。
	(*ppRRS)->n_job = i + 1/*自分*/;
	//コルーチンを作成する。
	co_thread_init();	//pcl.モジュールによるPCLの実装ではco_thread_init()を複数回呼び出しても安全です。従ってアプリケーションが独自にco_thread_init()を使用している場合もRRSのPCLによる実装は使用可能です。
	for(i = 0; i < (*ppRRS)->n_job - 1/*自分*/; i++) {
		(*ppRRS)->TBL_co[i] = co_create((void (*)(void*))aFn[i], *ppRRS, NULL, stacksize);
		if(!(*ppRRS)->TBL_co[i]) { DIE(); }
	}
	(*ppRRS)->TBL_co[i/*=((*ppRRS)->n_job-1)*/] = co_current();	//自分
	if(!(*ppRRS)->TBL_co[i/*=((*ppRRS)->n_job-1)*/]) { DIE(); }
	//最初のジョブをアクティブにする。
	(*ppRRS)->i_job = i/*=((*ppRRS)->n_job-1)*/;
	RRS_yield(*ppRRS);
	//ここで自分の順番になっています。
}
void RRS_yield(ST_RRS* pRRS) {
	//順番を次へ進める。
	if(++pRRS->i_job == pRRS->n_job) { pRRS->i_job = 0; }
	//次のジョブをアクティブにする。
	co_call(pRRS->TBL_co[pRRS->i_job]);
	//ここで自分の順番になっています。
}
#endif//RRS_USE_PCL
/****************************************************************************
 *	プラットフォーム依存部分 - RXマイコンによる実装
 ****************************************************************************/
#ifdef  RRS_USE_RXMCU
// * Fri Sep 30 21:41:38 JST 2016 Naoyuki Sawa
// - RRS_new_v(),RRS_yield()の、Hew用の実装を作成しました。
//   「/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z」の、2016/09/30バージョンを利用しています。
//   動的メモリ割り当てを避けるために、スタックバッファサイズは固定としました。
// * Sat Oct 01 21:11:57 JST 2016 Naoyuki Sawa
// - RRS_new_v(),RRS_yield()のHew用の実装で、Context.srcをバイナリ化してC言語ソースに埋め込みました。
//   もう、Context.hとContext.srcを、プロジェクトに含める必要は無くなりました。
//   「/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z」の、2016/10/01バージョンを利用しています。
//   詳細は、/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z内のREADME.TXTの、同日のコメントを参照して下さい。
//{{2016/10/01変更:RRS_new_v(),RRS_yield()のHew用の実装で、Context.srcをバイナリ化してC言語ソースに埋め込みました。
//#include "Context.h"	//「/clip/keep/RXマイコン用_コンテキストスイッチ処理.7z」の、2016/09/30バージョンを利用しています。
//↓2016/10/01変更:RRS_new_v(),RRS_yield()のHew用の実装で、Context.srcをバイナリ化してC言語ソースに埋め込みました。
/*static*/ const unsigned char TBL_Context_src[]={0xEF,0x05,0xFF,0x20,0x21,0x7E,0xA3,0x7E,0xA4,0x39,0x06,0x00,0x7E,0xB1,0x02,0x6E,0x6D,0xE3,0x10,0xEF,0x50,0x02,0x6E,0x6D,0x06,0xA0,0x10,0x10,0x6F,0x6D,0x02};	//staticにするとHewが「C5550 (W) Variable "TBL_Context_src" was set but never used」という警告を出すので仕方無くstaticを削除しました。どうやらHewは配列データをコードとして使用しても使用されている事を認識出来ないようです。
#define Context_Init(StackBuffer, StackBufferBytes, StartupFunction, StartupArgument)	(*(void (*)(void*, int, void (*)(void*), void*))(&TBL_Context_src[0]))((StackBuffer), (StackBufferBytes), (StartupFunction), (StartupArgument))
#define Context_Switch(StackBuffer)							(*(void (*)(void*))(&TBL_Context_src[22]))(StackBuffer)
//}}2016/10/01変更:RRS_new_v(),RRS_yield()のHew用の実装で、Context.srcをバイナリ化してC言語ソースに埋め込みました。
#ifndef StackBufferBytes
#define StackBufferBytes	4096	//調整可
#endif//StackBufferBytes
/*typedef*/ struct _ST_RRS {
	uint16_t	n_job;			//ジョブの数					   =RRS_new_*()に指定した関数の数+1/*RRS_new_*()を呼び出したコンテキストのジョブ
	uint16_t	i_job;			//現在実行中のジョブの順番			 0 =RRS_new_*()に指定した1番目の関数のジョブ      , 1 =RRS_new_*()に指定した2番目の関数のジョブ      ,…, n_job-1 = RRS_new_*()を呼び出したコンテキストのジョブ
	int		StackBuffer[StackBufferBytes/sizeof(int)];	//副コンテキスト用のスタックバッファ
} /*ST_RRS*/;
/*--------------------------------------------------------------------------*/
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/) {
	//Hew用の実装は、シングルインスタンスです。
	static ST_RRS stRRS;
	if(stRRS.n_job) { DIE(); }	//Hew用の実装では、RRS_new_v()を二回以上呼び出す事は出来ません。
	if(stacksize && (stacksize != StackBufferBytes)) { DIE(); }	//Hew用の実装では、スタックサイズの指定を省略する(stacksize==0)か、又は、StackBufferBytesに一致するスタックサイズ(stacksize==StackBufferBytes)しか、指定出来ません。
	{
		//関数の数(=自分以外のジョブの数)を数える。
		int i = 0;
		while(aFn[i]) { i++; }
		if(i > 1) { DIE(); }	//Hew用の実装では、「主コンテキスト+副コンテキスト0個(i==0)」,又は,「主コンテキスト+副コンテキスト1個(i==1)」の構成しか、指定出来ません。
		//構造体のポインタを格納する。
		*ppRRS = &stRRS;
		//ジョブの数を格納する。
		(*ppRRS)->n_job = i + 1/*自分*/;
		//副コンテキストが有れば…
		if((*ppRRS)->n_job == 2) {
			//副コンテキストを作成する。
			Context_Init((*ppRRS)->StackBuffer, StackBufferBytes, (void (*)(void*))aFn[0], *ppRRS);
		}
		//最初のジョブをアクティブにする。
		(*ppRRS)->i_job = i/*=((*ppRRS)->n_job-1)*/;
		RRS_yield(*ppRRS);
		//ここで自分の順番になっています。
	}
}
void RRS_yield(ST_RRS* pRRS) {
	//順番を次へ進める。
	if(++pRRS->i_job == pRRS->n_job) { pRRS->i_job = 0; }
	//副コンテキストが有れば…
	if(pRRS->n_job == 2) {
		//次のジョブをアクティブにする。
		Context_Switch(pRRS->StackBuffer);
	}
	//ここで自分の順番になっています。
}
#endif//RRS_USE_RXMCU
/****************************************************************************
 *	プラットフォーム共通部分
 ****************************************************************************/
#ifndef PIECE
void RRS_new_l(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, ...) {
	va_list ap;
	va_start(ap, stacksize);
	RRS_new_a(ppRRS, stacksize, ap);
	va_end(ap);
}
void RRS_new_a(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, va_list ap) {
	void (*aFn[(7+1)/*調整可*/])(ST_RRS*);	//実用上、ジョブの数が7以下(終端(NULL)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	int i = 0;
	for(;;) {
	    //	if(!(aFn[i] =                    va_arg(ap, void (*)(ST_RRS*)))) { break; }	//←本当はこうだが、va_arg()に関数ポインタを指定出来ないので…
#ifndef __RENESAS__
//□Win32とP/ECEの場合
		if(!(aFn[i] =                    va_arg(ap, void  *          ))) { break; }	//←こうした。生成されるコードは同じです。
#else //__RENESAS__
//□Hewの場合
		if(!(aFn[i] = (void (*)(ST_RRS*))va_arg(ap, void  *          ))) { break; }	//←こうした。生成されるコードは同じです。	(void*)を関数ポインタに代入するのは安全なはずなのだが、Hewでは何故か「C5513 (W) A value of type "void *" cannot be assigned to an entity of type "void (*)(ST_RRS *)"」という警告が出る。仕方が無いので、Hewの場合だけ明示的にキャストする事にした。
#endif//__RENESAS__
		if(++i >= ARRAY_SIZE(aFn)) { DIE(); }	//ここで停止した場合は、aFn[]不足か,又は,呼出し側が終端を忘れている。おそらく後者だろう。
	}
	RRS_new_v(ppRRS, stacksize, aFn);
}
#else //PIECE
void RRS_new_l(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, ...);
void RRS_new_a(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, va_list ap);
asm("
		.code
		.align		1
		.global		RRS_new_l
		.global		RRS_new_a
RRS_new_l:
		;//%r12     := ppRRS
		;//[%sp+ 0] := retp
		;//[%sp+ 4] := stacksize
		;//[%sp+ 8] := ...
		xld.w		%r13, [%sp+4]				;//%r13 := stacksize
		xadd		%r14, %sp, 8				;//%r14 := ap = &...
RRS_new_a:
		;//%r12     := ppRRS
		;//%r13     := stacksize
		;//%r14     := ap
		xjp		RRS_new_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
int RRS_n_job(ST_RRS* pRRS) {
	//ジョブの数を返す。
	return pRRS->n_job;
}
int RRS_i_job(ST_RRS* pRRS) {
	//現在実行中のジョブの順番を返す。
	return pRRS->i_job;
}
/****************************************************************************
 *	注意事項
 ****************************************************************************/
#if 0
//{{2016/02/19削除:この注意事項は古くなりました。もう、アプリーションがこの点に注意する必要はありません。
//■注意
//　pRRSをグローバル変数に格納し、各ジョブが'引数pRRS'ではなく'グローバル変数pRRS'を参照する場合、'グローバル変数pRRS'を格納するタイミングに注意せよ。
//□間違った例
//　RRS_new_l()が処理を返すまで、RRS_new_l()の'戻り値pRRS'は受け取られない。この点を忘れると、以下のバグが発生する。
//　│ST_RRS* pRRS;			//グローバル変数
//　│void app_main() {
//　│  pRRS = RRS_new_l(0,jobA,NULL);	//この関数が処理を返す前にjobA()が呼び出される。
//　│  for(;;) { jobB_subr(); }
//　│}
//　│void jobA(ST_RRS* pRRS) {
//　│  for(;;) { jobA_subr(); }
//　│}
//　│void jobA_subr() {
//　│  puts("jobA_subr");
//　│  RRS_yield(pRRS);			//'グローバル変数pRRS'を参照している。初回にここへ来た時'グローバル変数pRRS'はまだ格納されていないので、NULL逆参照エラーが発生する。
//　│}
//　│void jobB_subr() {
//　│  puts("jobB_subr");
//　│  RRS_yield(pRRS);			//'グローバル変数pRRS'を参照している。
//　│}
//□正しい例①
//　pRRSをグローバル変数に格納しなければ、前述のバグは発生しない。
//　│void app_main() {
//　│  ST_RRS* pRRS = RRS_new_l(0,jobA,NULL);
//　│  for(;;) {
//　│    puts("jobB_subr");
//　│    RRS_yield(pRRS);		//RRS_new_l()が返した'ローカル変数pRRS'を参照している。
//　│  }
//　│}
//　│void jobA(ST_RRS* pRRS) {
//　│  for(;;) {
//　│    puts("jobA_subr");
//　│    RRS_yield(pRRS);		//jobA()の'引数pRRS'を参照している。
//　│  }
//　│}
//□正しい例②
//　pRRSをグローバル変数に格納する場合、最初のジョブの処理の先頭で格納するのが安全だ。
//　│ST_RRS* pRRS;			//グローバル変数
//　│void app_main() {
//　│  /*ST_RRS* pRRS=*/ RRS_new_l(0,jobA,NULL);	//RRS_new_l()の戻り値は、既にjobA()が'グローバル変数pRRS'に格納した値と同じなので、RRS_new_l()の戻り値を受け取る必要は無い。
//　│  for(;;) {
//　│    puts("jobB_subr");
//　│    RRS_yield(pRRS);		//'グローバル変数pRRS'を参照している。
//　│  }
//　│}
//　│void jobA(ST_RRS* _pRRS) {
//　│  pRRS = _pRRS;			//'グローバル変数pRRS'を格納する。
//　│  for(;;) {
//　│    puts("jobA_subr");
//　│    RRS_yield(pRRS);		//'グローバル変数pRRS'を参照している。
//　│  }
//　│}
//}}2016/02/19削除:この注意事項は古くなりました。もう、アプリーションがこの点に注意する必要はありません。
#endif
//{{2016/02/19追記
#if 0
しばらくの間、上記の方法でアプリケーション側で回避していたのですが、うっかり間違えてバグの温床になり易いので、やはり当モジュール側で回避する事にしました。
そのために、RRS_new_l(),RRS_new_a(),RRS_new_v()の引数を変更しました。
変更前は構造体のポインタを戻り値で返していましたが、変更後は第一引数で指定された変数に格納するようにしました。
今後は、RRS_new_v()が構造体のメモリを確保した直後に第一引数で指定された変数に構造体のポインタを格納するので、アプリケーション側で意識する必要は有りません。
□変更後の例
　│ST_RRS* pRRS;			//グローバル変数
　│void app_main() {
　│  RRS_new_l(&pRRS,0,jobA,NULL);	//RRS_new_l()がが構造体のメモリを確保した直後にpRRSに構造体のポインタが格納される。
　│  for(;;) { jobB_subr(); }
　│}
　│void jobA(ST_RRS* pRRS) {
　│  for(;;) { jobA_subr(); }
　│}
　│void jobA_subr() {
　│  puts("jobA_subr");
　│  RRS_yield(pRRS);			//'グローバル変数pRRS'を参照している。初回はRRS_new_l()が処理を返す前に呼び出されるが、その時点で'グローバル変数pRRS'は既に格納されてるので、正しく動作する。
　│}
　│void jobB_subr() {
　│  puts("jobB_subr");
　│  RRS_yield(pRRS);			//'グローバル変数pRRS'を参照している。
　│}
#endif
//}}2016/02/19追記
/****************************************************************************
 *	ジョブ同期用の関数
 ****************************************************************************/
/*typedef*/ struct _ST_RRS_sync_node {
	int			done;		//バリアが解除された事を示すフラグ
	ST_RRS_sync_node*	next;		//ノードチェインの次のノード
} /*ST_RRS_sync_node*/;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void RRS_sync(ST_RRS* pRRS, ST_RRS_sync_root* root) {
//{{2016/09/30追加:RRS_sync_idle()を追加しました。
	//RRS_sync()は、アイドリング関数を指定せずにRRS_sync_idle()を呼び出す事と等価です。
	RRS_sync_idle(pRRS, root, NULL, NULL);
}
void RRS_sync_idle(ST_RRS* pRRS, ST_RRS_sync_root* root, void (*idle/*NULL可*/)(void* arg), void* arg) {
//}}2016/09/30追加:RRS_sync_idle()を追加しました。
	ST_RRS_sync_node node;
	//既に、到着したジョブの数が、同期させるジョブの数以上になっていたら、エラーです。
	// - もしここで停止したら、アプリケーションがroot->reqの初期化を忘れて、(root->req==0)である可能性が有ります。
	if(root->cnt >= root->req) { DIE(); }
	//ローカル変数nodeを、到着したジョブのノードチェインのチェインする。
	// - チェインの順序は結果に影響しないので、先頭にチェインしても末尾にチェインしても構いません。
	//   先頭にチェインする方が効率が良いので、現在の実装では、先頭にチェインするようにしています。
	node.done = 0;	//忘れないで!!
	node.next = root->first;
	            root->first = &node;
	//到着したジョブの数を増やして、同期させるジョブの数に到達したら…
	if(++root->cnt == root->req) {
		//到着したジョブのノードチェインにチェインされている、全てのノードに、バリアが解除された事を示すフラグをセットする。
		do {
#ifdef  _DEBUG
			//デバッグビルドならば、ノードチェインの長さと到着したジョブの数が一致している事を確認するために、到着したジョブの数を減らして行く。
			// - このループを抜けた所で、到着したジョブの数が0になったかを確認します。
			root->cnt--;
			//デバッグビルドならば、バリアが解除された事を示すフラグがまだセットされていない事を確認する。
			// - もしここで停止したら、当モジュールのバグです。
			if(root->first->done) { DIE(); }
#endif//_DEBUG
			root->first->done = 1;
			root->first = root->first->next;
		} while(root->first);
#ifdef  _DEBUG
		//デバッグビルドならば、ノードチェインの長さと到着したジョブの数が一致している事を確認する。
		// - もしここで停止したら、当モジュールのバグか,又は,別のジョブが誤って使用中のrootを再初期化した可能性が有ります。	⇒{{2016/06/16コメント追記:左記のバグは、'Thu Jun 16 20:40:56 JST 2016'に追加した「'バリアが解除された事を示すフラグがセットされるまでループして待つ'処理の中で、ノードチェインが破壊されていないかを検査するデバッグ処理」の方で先に検出されると思いますが、より安全のためにここでの検査も残しておく事にしました。}}
		//   原則としては、rootをグローバル変数として定義し、reqフィールドを静的に初期化しておけば、アプリケーションがrootを明示的に再初期化する処理は必要無いはずです。
		if(root->cnt) { DIE(); }
#endif//_DEBUG
		root->cnt = 0;	//デバッグビルドでは上で既に(root->cnt=0)である事を確認済みなので、この処理はダミー処理となります。
	}
	//～～～～～～～～～～■■重要!!■■当関数内のここより下の処理では、rootを参照してはいけません。先行してバリア解除されたジョブが、同じrootを使って次の同期を開始している可能性が有るからです。～～～～～～～～～～
	//バリアが解除された事を示すフラグがセットされるまでループして待つ。
	// - 今回のRRS_sync()呼び出しによって到着したジョブの数が同期させるジョブの数に到達した場合は、上の処理で既にバリアが解除された事を示すフラグがセットされており、下記のループは一度も実行しません。
	while(!node.done) {
#ifdef  _DEBUG
		// * Thu Jun 16 20:40:56 JST 2016 Naoyuki Sawa
		// - 'バリアが解除された事を示すフラグがセットされるまでループして待つ'処理の中で、ノードチェインが破壊されていないかを検査するデバッグ処理を追加しました。
		//   詳細は、以下の通りです。
		// - 上で、「■■重要!!■■当関数内のここより下の処理では、rootを参照してはいけません。」と書いたのですが、厳密には、ここでrootを参照する事は可能です。
		//   なぜなら、'while(!node.done)'がFalseと判定された直後なので、まだこのrootの今回のバリア解除が行われていない事が確実だからです。
		//   従って、ここで、rootからノードチェインをたどって検査を行う事は可能です。
		//   (毎ループの、'while(!node.done)'がFalseと判定された直後，'RRS_yield(pRRS)'を呼ぶ前に行う必要が有る事に注意して下さい!!'RRS_yield(pRRS)'を呼ぶと、その中でバリア解除が行われる可能性が有るからです。)
		// - 検査する点としては、ノードチェインに自分のノードが含まれているかを確認する事にしました。
		//   何らかのバグで、ノードチェインから自分のノードが外れると、バリア解除が行われた時に自分のノードのフラグがセットされず、永久に待ち続けるという検出しづらい症状になるので、その前に発見したいからです。
		//   ノードチェインから自分のノードが外れるバグが生じるケースとしては、使用中のrootを他のジョブが再初期化した場合などです。
		//   <例>
		//   │ST_RRS_sync_root r = {2};
		//   │void job1(ST_RRS* pRRS) { RRS_sync(pRRS, &r); ～ }
		//   │void job2(ST_RRS* pRRS) { r = (ST_RRS_sync_root){2}; }	←job1がrでバリア解除待ちしている途中に、rを再初期化しているバグです。この後、job1のRRS_sync()のループ処理の中で、バグ検出されるはずです。
		ST_RRS_sync_node* pnode;
		for(pnode = root->first; pnode; pnode = pnode->next) {	//ノードチェインをたどって…
			if(pnode == &node) { break; }			//自分のノードが含まれていたら抜ける。
		}
		if(!pnode) { DIE(); }					//自分のノードが含まれていなかったら、エラー停止する。
#endif//_DEBUG
//{{2016/09/30追加:RRS_sync_idle()を追加しました。
		//アプリケーション定義のアイドリング関数が指定されていたら、アイドリング関数を呼び出す。
		// - アプリケーション定義のアイドリング関数の中では、RRS_yield()を呼び出さないように注意して下さい。(要するに、アイドリング関数としてschedule()を指定してはいけないと言う事です。)	┐
		//   もし、アプリケーション定義のアイドリング関数の中でRRS_yield()が呼び出されると、この下で行うRRS_yield()呼び出しと合わせて、二回RRS_yield()が行われてしまいます。			├【重要】
		//   必ずしも致命的な問題になるとは限らないのですが、このジョブの性能が低下したり、アプリケーションが特定の同期タイミングを想定している場合は致命的な問題になる可能性も有ります。	┘
		// - 即、同期完了した場合は、このブロックを通らないので、アイドリング関数を一度も呼び出しません。
		//   そうしないと、RRS_sync_idle()を呼び出す度に必ずアイドリング関数が一回実行されて、性能が低下してしまう問題が発生するからです。
		if(idle) { (*idle)(arg); }
//}}2016/09/30追加:RRS_sync_idle()を追加しました。
		//スケジューリングを実行する。
		RRS_yield(pRRS);
	}
}
/****************************************************************************
 *	ジョブ同期用の関数の使用例
 ****************************************************************************/
#if 0
//□テストプログラム
	#define TEST_SYNC	//このシンボルを定義しなければ、同期無しでテスト実行します。このシンボルを定義すると、同期有りでテスト実行します。
void job1(ST_RRS* pRRS);	//前方参照
void job2(ST_RRS* pRRS);	//前方参照
void job3(ST_RRS* pRRS);	//前方参照
void job4(ST_RRS* pRRS);	//前方参照
int app_main(int argc, char* argv[]) {
	int exitCode = EXIT_SUCCESS;
	RRS_new_l(&pRRS, 0/*stacksize*/,
		job1,
		job2,
		job3,
		job4,
		NULL/*終端*/);
	while(!_kbhit()) { RRS_yield(pRRS); }
	return exitCode;
}
#ifdef  TEST_SYNC
ST_RRS_sync_root sr3 = { 3 };	//3ジョブ同期用	┐この例では、同期するジョブ数毎にrootを設けましたが、必ずしも、同期するジョブ数毎に一つのrootを設ける訳では有りません。同期する組毎に、一組のrootを設けて下さい。
ST_RRS_sync_root sr4 = { 4 };	//4ジョブ同期用	┘例えば、全体で4ジョブが有り、2ジョブ×2毎の組で同期を取る場合は、2ジョブ同期用のrootを二組設けて下さい。ただし、二組の同期が同時に発生しない場合は、全体で一組のrootを共用しても構いません。
#endif//TEST_SYNC
void job1(ST_RRS* pRRS) {
	int i;
	puts("job1 start");
	//5サイクルかかる処理を実行する想定
	for(i = 0; i < 5; i++) { RRS_yield(pRRS); }
	puts("job1 calcA done");
#ifdef  TEST_SYNC
	//3ジョブ同期
	RRS_sync(pRRS, &sr3);
	puts("job1 syncA done");
#endif//TEST_SYNC
	//9サイクルかかる処理を実行する想定
	for(i = 0; i < 9; i++) { RRS_yield(pRRS); }
	puts("job1 calcB done");
#ifdef  TEST_SYNC
	//3ジョブ同期
	RRS_sync(pRRS, &sr3);
	puts("job1 syncB done");
	//4ジョブ同期
	RRS_sync(pRRS, &sr4);
#endif//TEST_SYNC
	puts("job1 end");
	for(;;) { RRS_yield(pRRS); }
}
void job2(ST_RRS* pRRS) {
	int i;
	puts("job2 start");
	//7サイクルかかる処理を実行する想定
	for(i = 0; i < 7; i++) { RRS_yield(pRRS); }
	puts("job2 calcA done");
#ifdef  TEST_SYNC
	//3ジョブ同期
	RRS_sync(pRRS, &sr3);
	puts("job2 syncA done");
#endif//TEST_SYNC
	//3サイクルかかる処理を実行する想定
	for(i = 0; i < 3; i++) { RRS_yield(pRRS); }
	puts("job2 calcB done");
#ifdef  TEST_SYNC
	//3ジョブ同期
	RRS_sync(pRRS, &sr3);
	puts("job2 syncB done");
	//4ジョブ同期
	RRS_sync(pRRS, &sr4);
#endif//TEST_SYNC
	puts("job2 end");
	for(;;) { RRS_yield(pRRS); }
}
void job3(ST_RRS* pRRS) {
	puts("job3 start");
#ifdef  TEST_SYNC
	//3ジョブ同期
	RRS_sync(pRRS, &sr3);
	puts("job3 syncA done");
	//3ジョブ同期
	RRS_sync(pRRS, &sr3);
	puts("job3 syncB done");
	//4ジョブ同期
	RRS_sync(pRRS, &sr4);
#endif//TEST_SYNC
	puts("job3 end");
	for(;;) { RRS_yield(pRRS); }
}
void job4(ST_RRS* pRRS) {
	puts("job4 start");
#ifdef  TEST_SYNC
	//4ジョブ同期
	RRS_sync(pRRS, &sr4);
#endif//TEST_SYNC
	puts("job4 end");
	for(;;) { RRS_yield(pRRS); }
}
//──────────────────────────────────────
//□同期無しでテスト実行した結果(TEST_SYNC定義無し)
job1 start		job1が開始し、5サイクルかかる処理の実行を開始した。
job2 start		job2が開始し、7サイクルかかる処理の実行を開始した。
job3 start		job3が開始した。
job3 end		job3は何もしないジョブなので、すぐに終了した。
job4 start		job4が開始した。
job4 end		job4も何もしないジョブなので、すぐに終了した。
job1 calcA done		job1の5サイクルかかる処理が完了した。引き続きすぐに、9サイクルかかる処理の実行を開始した。	←絶対時刻=5
job2 calcA done		job2の7サイクルかかる処理が完了した。引き続きすぐに、3サイクルかかる処理の実行を開始した。	←絶対時刻=7
job2 calcB done		job2の3サイクルかかる処理が完了した。								←絶対時刻=7+3=10
job2 end		job2が終了した。
job1 calcB done		job1の9サイクルかかる処理が完了した。								←絶対時刻=5+9=14
job1 end		job1が終了した。
//□同期有りでテスト実行した結果(TEST_SYNC定義有り)
job1 start		job1が開始し、5サイクルかかる処理の実行を開始した。
job2 start		job2が開始し、7サイクルかかる処理の実行を開始した。
job3 start		job3が開始し、3ジョブ同期を開始した。
job4 start		job4が開始し、4ジョブ同期を開始した。
job1 calcA done		job1の5サイクルかかる処理が完了した。3ジョブ同期を開始した。					←絶対時刻=5
job2 calcA done		job2の7サイクルかかる処理が完了した。3ジョブ同期を開始した。					←絶対時刻=7
job2 syncA done		job2の、3ジョブ同期が解除された。引き続きすぐに、3サイクルかかる処理の実行を開始した。
job3 syncA done		job3の、3ジョブ同期が解除された。引き続きすぐに、3ジョブ同期を開始した。						┐job1の1回目の3ジョブ同期のRRS_sync(pRRS,&sr3)が制御を返す前に、job3が同じsr3を使って2回目の3ジョブ同期RRS_sync(pRRS,&sr3)を開始している事に注目せよ。
job1 syncA done		job1の、3ジョブ同期が解除された。引き続きすぐに、9サイクルかかる処理の実行を開始した。					┘sr3の使用期間が、1回目の3ジョブ同期と2回目の3ジョブ同期で重複しているが、問題無い。詳細は、RRS_sync()関数内の「■■重要!!■■」のコメントを参照せよ。
job2 calcB done		job2の3サイクルかかる処理が完了した。3ジョブ同期を開始した。					←絶対時刻=7+3=10
job1 calcB done		job1の9サイクルかかる処理が完了した。3ジョブ同期を開始した。					←絶対時刻=7+9=16
job1 syncB done		job1の、3ジョブ同期が解除された。引き続きすぐに、4ジョブ同期を開始した。
job2 syncB done		job2の、3ジョブ同期が解除された。引き続きすぐに、4ジョブ同期を開始した。
job3 syncB done		job3の、3ジョブ同期が解除された。引き続きすぐに、4ジョブ同期を開始した。
job3 end		job3の、4ジョブ同期が解除された。job3が終了した。
job4 end		job4の、4ジョブ同期が解除された。job4が終了した。
job1 end		job1の、4ジョブ同期が解除された。job1が終了した。
job2 end		job2の、4ジョブ同期が解除された。job2が終了した。
#endif
