#include "clip.h"
/****************************************************************************
 *	Pthreads-win32
 ****************************************************************************/
#if     (defined(GC_H) && defined(PTHREAD_H))
static void GC_thread_exit_proc(void* arg) {
	GC_unregister_my_thread();//GCからこのスレッドを登録解除する。
}
static void* GC_pthread_start_inner(struct GC_stack_base* sb, void* arg) {
	void* retval;
	void** start_arg = arg;
	void* (*start)(void*) = start_arg[0];
	arg = start_arg[1];
	GC_free(start_arg);//(*start)()がpthread_exit()やpthread_testcancel()で終了した場合は処理を返さないので、(*start)()を呼び出す前にstart_argのメモリを開放しておく必要が有る。
	GC_register_my_thread(sb);//GCにこのスレッドを登録する。
	pthread_cleanup_push(GC_thread_exit_proc, NULL);
	retval = (*start)(arg);
	pthread_cleanup_pop(1);//(*start)()が処理を返した場合は明示的にクリーンアップハンドラーを実行する。(*start)()がpthread_exit()やpthread_testcancel()で終了した場合は自動的にクリーンアップハンドラーが呼び出される。詳細は「Linux Programmer's Manual - PTHREAD_CLEANUP_PUSH」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/pthread_cleanup_push.3.html)を参照せよ。
	return retval;
}
static void* GC_pthread_start(void* arg) {
	return GC_call_with_stack_base(GC_pthread_start_inner, arg);
}
int GC_pthread_create(pthread_t* tid, const pthread_attr_t* attr, void* (*start)(void*), void* arg) {
	void** start_arg = GC_malloc_uncollectable(sizeof(void*) * 2);	//GC_pthread_start()が開始する前に当関数が終了する可能性が有るのでstart_argをスタック上に取ってはいけない。
	start_arg[0] = start;
	start_arg[1] = arg;
	return (*pthread_create)(tid, attr, GC_pthread_start, start_arg);
    //         ~~~~~~~~~~~~~~~~~マクロの置換を抑制するためにこのように書いた。
}
#endif//(defined(GC_H) && defined(PTHREAD_H))
/****************************************************************************
 *	GCとスレッドを併用するために必要な初期化処理
 ****************************************************************************/
// * Mon May 30 21:45:50 JST 2016 Naoyuki Sawa
// - GCとスレッドを併用するために必要な初期化処理を、ライブラリ内へ移しました。
//   これまでは、各アプリケーションのmain()関数の中で初期化処理を実行していましたが、必ず必要な処理なので、ライブラリ内で初期化処理を実行する方が適切だと思ったからです。
// - 軽く動作確認をして、これまでと変わらずGCとスレッドを併用出来る事を確認しました。
//   従って、下記の初期化処理は、main()が呼び出される前に実行しても安全だと思います。
// - 下記の初期化処理を、複数回実行しても、動作に支障は有りません。
//   Boehm GCの実装を確認した所、GC_init()は既に初期化されていたら何もせずに処理を返しますし、GC_allow_register_threads()はフラグをセットするだけなので何度実行しても構わないからです。
//   従って、既存のmain()関数の中で初期化処理を実行しているアプリケーションは、そのままでも動作上は問題ありません。
//   しかし、初期化処理を呼び出している個所が複数有ると紛らわしいので、今後、個々の既存のアプリケーションを保守する際、ついでに、main()関数の中で下記と同じ初期化処理を実行している部分を削除して下さい。	←←←←←←←←←←※TODO:
#if     (defined(GC_H) && defined(PTHREAD_H))
#if     (_MSC_VER > 1200)	//Visual C++ .NETならば…
static int  GC_threads_init() {
#else //(_MSC_VER > 1200)	//Visual C++ 6.0 ならば…
static void GC_threads_init() {
#endif//(_MSC_VER > 1200)
    //{{GCとスレッドを併用するために必要な初期化処理
	GC_init();//この中でGCスレッドが起動する。メインスレッドをpthread_exit()等で終了するとGCスレッドが残ってプロセスが終了しないので注意せよ。
	GC_allow_register_threads();
    //}}GCとスレッドを併用するために必要な初期化処理
#if     (_MSC_VER > 1200)	//Visual C++ .NETならば…
	return 0;	//VC++2015では、main()が呼び出される前に実行する関数が0を返すと、main()を呼び出さずに終了する。VC++6.0では、main()が呼び出される前に実行する関数の戻り値は検査されない。
#endif//(_MSC_VER > 1200)
}
//{{2016/09/07追加:64ビット版では、「#pragma section(".CRT$XIU", long, read)」を追加しました。
#ifdef  _WIN64
#pragma section(".CRT$XIU", long, read)	//x64ではこの指定を追加しないと初期化関数が呼び出されません。詳細は/clip/test.win/keep/開発メモ.txtの'Thu Aug 11 23:59:25 JST 2016'のコメントを参照して下さい。
#endif//_WIN64
//}}2016/09/07追加:64ビット版では、「#pragma section(".CRT$XIU", long, read)」を追加しました。
#pragma data_seg(".CRT$XIU")	//'#pragma data_seg(".CRT$XCU")'でも構いません。
//{{2016/09/08変更:init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。詳細は、cliplibc.h の'Thu Sep 08 22:20:43 JST 2016'のコメントを参照して下さい。
//static void* /*!!const不可!!*/ pGC_threads_init/*変数名は関係無いので何でも良い*/ = GC_threads_init;	//このポインタが".CRT$XI?"セグメント内に配置されて、mainCRTStartup()がポインタの指す関数を呼び出します。
//↓2016/09/08変更:init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。詳細は、cliplibc.h の'Thu Sep 08 22:20:43 JST 2016'のコメントを参照して下さい。
/*static*/ void* /*!!const不可!!*/ pGC_threads_init/*変数名は関係無いので何でも良い*/ = GC_threads_init;	//このポインタが".CRT$XI?"セグメント内に配置されて、mainCRTStartup()がポインタの指す関数を呼び出します。
//}}2016/09/08変更:init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。詳細は、cliplibc.h の'Thu Sep 08 22:20:43 JST 2016'のコメントを参照して下さい。
#pragma data_seg()		//元のセグメントに戻す。忘れないで!!
#endif//(defined(GC_H) && defined(PTHREAD_H))
