コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2023年12月2日 >>> 2023年12月2日
link もっと後

2023年12月2日

musl libcのpthread_barrier_wait()の実装 その2、LastとOthers

目次: C言語とlibc

前回の続きです。musl libc 1.2.4のpthread_barrier_wait()の実装と動作の概要です。バリアに到達したスレッドがNスレッド(N >= 2)あるとすると、大きく分けて3つの役割に分かれます。役割名は私が適当に付けました。正式な名前はあるのかな?

  • Owner: 最初にバリアに突入してきたスレッド(1スレッド)
  • Last: 最後にバリアに突入してきたスレッド(1スレッド)
  • Others: OwnerでもLastでもないスレッド全て(0〜N-2スレッド、平たく言えば3スレッド以上のバリアのときだけ存在する)

いっぺんに説明すると意味不明なので、順番に説明します。今回はLastとOthersです。

LastとOthers(到達側)

自分がLastかどうか判定する条件はバリア変数の_b_instがNULL以外であり、インスタンスのカウントがバリア同期するスレッド数と一致(= 最後に到達した1スレッド)することです。Lastのやることはバリア変数(pthread_barrier_t)に設定されたインスタンスを消すことと、バリア同期を待っているOthersを起こすことです。

Lastがこの時点でb->_b_instをNULLにしていること、ローカル変数instを使っていてb->_b_instを使わないことには理由があって、次のバリア処理とのオーバーラップと関係しています。同時に説明するとややこしいので、次回ご説明しようと思います。

OwnerとLast以外は全部Othersスレッドとなります。OthersのやることはLastが来るまで待つことです。

LastとOthersの到達側に関連するコードは下記のとおりです。

pthread_barrier_wait()のLastとOthersの到達側のコード

// musl/src/thread/pthread_barrier_wait.c

int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	/* Trivial case: count was set at 1 */
	if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;

	/* Process-shared barriers require a separate, inefficient wait */
	if (limit < 0) return pshared_barrier_wait(b);

	/* Otherwise we need a lock on the barrier object */
	while (a_swap(&b->_b_lock, 1))
		__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
	inst = b->_b_inst;

	/* First thread to enter the barrier becomes the "instance owner" */
	if (!inst) {
		//★★Ownerのスレッドの処理は省略(その1をご覧ください)★★
	}

	/* Last thread to enter the barrier wakes all non-instance-owners */
	if (++inst->count == limit) {
		//★★Lastのスレッドはこちら★★
		b->_b_inst = 0;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		a_store(&inst->last, 1);
		if (inst->waiters)
			__wake(&inst->last, -1, 1);
	} else {
		//★★Othersのスレッドはこちら★★
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		__wait(&inst->last, &inst->waiters, 0, 1);
	}

細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。


pthread_barrier_wait()のLastとOthersの到達側シーケンスの一例

見たままなので解説することがないですね。全員でインスタンスのカウントを+1して、最後のスレッドLastだけが特殊な処理を行います。

LastとOthers(脱出側)

脱出側のやることは単純ですが役割分担が少しややこしいです。到達側と同様にLastとOthersに役割が分かれますが、到達側のLast = 脱出側のLastとは限らないからです。

脱出時のLastになる条件はインスタンスのカウント値が1であることです。到達時にLastであったかOthersであったかは無関係です。脱出時のLastのやることは、インスタンスのfinishedを+1して待機中のOwnerスレッドを再開させることです。

脱出時のOthersになる条件はLastではない、インスタンスのカウント値が1以外であることです。

LastとOthersの脱出側に関連するコードは下記のとおりです。

pthread_barrier_wait()のLastとOthersの脱出側のコード

// musl/src/thread/pthread_barrier_wait.c

int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	//★★略★★

	/* Last thread to exit the barrier wakes the instance owner */
	if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
		__wake(&inst->finished, 1, 1);

	return 0;
}

細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。


pthread_barrier_wait()のLastとOthersの脱出側シーケンスの一例

到達側のLastスレッドの処理では待機していたOthersスレッド達を全員再開させ、Lastも処理を再開します。するとLast + Othersスレッド全てがいっぺんに脱出側の処理を開始します。先ほど説明したとおり、どのスレッドが脱出側のLastになるかは運次第です。

実装の特徴はアトミックアクセスですかね。a_fetch_add(x, -1)はポインタxの指す先をアトミックに-1して、返り値でxの以前の値を返す関数です……といわれてもわかりにくいですよね。4スレッド(Owner, Others1, Others2, Last)の場合を書きましょうか。Ownerスレッドはカウント値を+1しないので、脱出処理開始時のカウント値は4 - 1 = 3です。

スレッド-1したあとのカウント値a_fetch_add()の返り値
Others 123
Others 212
Last 01

ちなみにアトミックアクセス以外の方法(if文とカウント値の変更など)では正常に動作しません。判定と値変更の間に他のスレッドが処理を行う可能性があるからです。

続きはまた今度。

編集者:すずき(2023/12/04 03:49)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2023年12月2日 >>> 2023年12月2日
link もっと後

管理用メニュー

link 記事を新規作成

<2023>
<<<12>>>
-----12
3456789
10111213141516
17181920212223
24252627282930
31------

最近のコメント5件

  • link 24年5月16日
    すずきさん (05/21 11:41)
    「あー、確かにdpkg-reconfigu...」
  • link 24年5月16日
    hdkさん (05/21 08:55)
    「システム全体のlocale設定はDebi...」
  • link 24年5月17日
    すずきさん (05/20 13:16)
    「そうですねえ、普通はStandardなの...」
  • link 24年5月17日
    hdkさん (05/19 07:45)
    「なるほど、そういうことなんですね。Exc...」
  • link 24年5月17日
    すずきさん (05/19 03:41)
    「Standardだと下記の設定になってい...」

最近の記事3件

  • link 24年5月21日
    すずき (05/23 23:19)
    「[Linux 6.1からLinux 6.6に手抜き更新したらハマった] 目次: 自宅サーバーLinux Kernelのlong...」
  • link 23年6月1日
    すずき (05/23 22:50)
    「[自宅サーバー - まとめリンク] 目次: 自宅サーバーこの日記システム、Wikiの話。カウンターをPerlからPHPに移植日...」
  • link 24年5月16日
    すずき (05/21 01:23)
    「[イマドキ?のlocale設定方法] 目次: 自宅サーバーLocaleの設定方法は未だによくわかってないのですが、最近ROCK...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 05/23 23:19