/*
 *	cliprrs.h
 *
 *	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_sync_idle()を追加しました。
 *	  これまで、RRS_sync()内でブロックされている間は、アプリケーション定義の周期処理が呼び出せない問題が有りました。
 *	  その問題を解決するために、ブロックされている間に呼び出されるアプリケーション定義のアイドリング関数を指定出来るバージョンの関数を追加しました。
 */
#ifndef __CLIP_RRS_H__
#define __CLIP_RRS_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
/****************************************************************************
 *	基本的な関数
 ****************************************************************************/
typedef struct _ST_RRS ST_RRS;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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);
void RRS_new_v(ST_RRS** ppRRS/*NULL不可*/, int stacksize/*0可*/, void (* const aFn[])(ST_RRS*)/*[fn1,[fn2,[...,fnN]]]NULL*/);
void RRS_yield(ST_RRS* pRRS);
int  RRS_n_job(ST_RRS* pRRS);
int  RRS_i_job(ST_RRS* pRRS);
/****************************************************************************
 *	ジョブ同期用の関数
 ****************************************************************************/
typedef struct _ST_RRS_sync_node ST_RRS_sync_node;	//opaque datatype
typedef struct _ST_RRS_sync_root {
	uint16_t		req;		//同期させるジョブの数			1以上の値で初期化しておく事。当モジュールの関数はこのフィールドを変更しない。
	uint16_t		cnt;		//到着したジョブの数			初期値は0とする事。RRS_sync()が呼び出される度に0→reqまで増加して行き、バリアが解除されたら0に戻る。		ノードチェインの長さと同じなので保持しなくても良いのだが、何度もノードチェインの長さを数える速度低下を避けるために、このフィールドに保持しておく事にした。
	ST_RRS_sync_node*	first;		//到着したジョブのノードチェイン	初期値はNULLとする事。各RRS_sync()呼び出しのローカル変数nodeをここにチェインして行き、バリアが解除されたらNULLに戻る。
} ST_RRS_sync_root;
//①ST_RRS_sync_rootをグローバル変数として静的に初期化して定義する場合は、以下の例のように定義して下さい。
//　│ST_RRS_sync_root syncRoot = { 3/*req(=同期させるジョブの数)*/ };
//　ほとんどの場合は、上記の定義方法で十分だと思います。
//  RRS_sync()を呼ぶとcnt,firstフィールドが変化しますが、バリア解除時に0に戻るので、アプリケーションが明示的に再初期化する必要は無いからです。
//②何らかの理由で、ST_RRS_sync_rootをグローバル変数として静的に初期化して定義出来ない場合や、異なるreqの値で再初期化したい場合は、以下の例のように行って下さい。
//  │memset(&syncRoot, 0, sizeof syncRoot);
//  │syncRoot.req = 3/*req(=同期させるジョブの数)*/;
//  又は
//  │syncRoot = (ST_RRS_sync_root){3/*req(=同期させるジョブの数)*/};	//GCC拡張機能を利用しています。
//  明示的な再初期化を行う場合は、使用中のsyncRootを再初期化しないように注意して下さい。
//  使用中のsyncRootを再初期化すると、RRS_sync()内の「'バリアが解除された事を示すフラグがセットされるまでループして待つ'処理の中で、ノードチェインが破壊されていないかを検査するデバッグ処理」が検出してエラー停止します。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void RRS_sync(ST_RRS* pRRS, ST_RRS_sync_root* root);
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()を追加しました。}}
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__CLIP_RRS_H__
