futex

というわけで、非同期リクエスト-レスポンスについて考える、つもりだったのだけど、なんかfutexというのを見てたので、そこらへんの話。


futexというのは、Linux で pthread の conditional variable を実装するための機能 + αといったところだろうか。


簡単な使いかたは↓こんな感じ。

#include <time.h>
#include <pthread.h>
#include <stdio.h>
#include <asm/unistd.h>
#include <errno.h>

#define FUTEX_WAIT 0
#define FUTEX_WAKE 1

static inline long
futex( volatile void *a, int op, int v,
       struct timespec *t )
{
	int r;
	asm __volatile__  ( "int $0x80"
			  : "=a"(r)
			  : "a"(__NR_futex), "b"(a), "c"(op), "d"(v), "S"(t)
			  : "memory" );
	return r;
}

volatile int key;

static
void *start( void *p )
{
	int r;
	struct timespec ts;

	ts.tv_sec = 1;
	ts.tv_nsec = 0;

	key = 0;
	/* *1 */
	r = futex( &key, FUTEX_WAIT, key, &ts );

	printf("%d\n",r);
	if ( r == -ETIMEDOUT ) {
		puts("1 byou inaini nanika site!!!");
	}


	return NULL;
}

int
main()
{
	pthread_t t;
	void *p;

	key = 0;

	pthread_create( &t, NULL, start, 0 );

	getchar();

	key++;
	futex( &key, FUTEX_WAKE, 1, 0 );

	pthread_join( t, &p );
}

変数のアドレスをキーにして、待ったり、起こしたりできる。
特徴としては、変数のアドレス、というのが、ユーザ空間のアドレスではなくて、カーネル空間でのアドレスで見てる、という点。
実際にマップされてるメモリが同じなら同じキーとみなされるので、shmでアタッチしたアドレスとか、MAP_SHAREDでmmapしたアドレスとかを使えば、他のプロセスとのやりとりも同様に行える。


あと、ただの共有メモリ以上の色々をやってくれるようで、ほぼユーザ空間で動くセマフォが作れるっぽい。例えば、上のコードでいうと、

	key = 0;
	/* #1 */
	r = futex( &key, FUTEX_WAIT, key, &ts );

この #1 の部分で、WAKEされても大丈夫なようになってる(引数に渡した key と &key の指してる値が変わってないか見てる)。


上では使ってないけど、「何か変数に値を設定したのち、待機してるのを起こす」というのがアトミックにできたりとか、「cache line ping-pong(て何?)」が起こらないように、待機してるスレッドを一度に全部起こすのではなくて、一旦、別の待機キューに移してから、一個ずつ起動とかができるよう(FUTEX_CMP_REQUEUE)。そこらへんは実際試してないので、理解があってるかどうかは知らない。


詳細は、http://people.redhat.com/drepper/futex.pdf あたり参照。