Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16902] ファイルの概要

このコミットは、Go言語のランタイムにおける runtime.SetFinalizer 関数の挙動を拡張し、ファイナライザ関数として func(interface{}) 型の関数を受け入れられるようにする変更です。これにより、特定の型に縛られずに、より汎用的なファイナライザを設定できるようになります。

コミット

commit 6350e45892b5b0189fe3461ba1e7f530da23ff8f
Author: Pieter Droogendijk <pieter@binky.org.uk>
Date:   Mon Jul 29 19:43:08 2013 +0400

    runtime: allow SetFinalizer with a func(interface{})
    
    Fixes #5368.
    
    R=golang-dev, dvyukov
    CC=golang-dev, rsc
    https://golang.org/cl/11858043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/6350e45892b5b0189fe3461ba1e7f530da23ff8f

元コミット内容

runtime: allow SetFinalizer with a func(interface{})

Fixes #5368.

R=golang-dev, dvyukov
CC=golang-dev, rsc
https://golang.org/cl/11858043

変更の背景

Go言語の runtime.SetFinalizer 関数は、ガベージコレクタがオブジェクトが到達不能になったと判断した際に実行されるファイナライザ関数を設定するために使用されます。この関数は、ファイルディスクリプタやネットワーク接続など、メモリ以外のリソースを解放する際に特に有用です。

このコミット以前は、SetFinalizer に渡すファイナライザ関数は、対象となるオブジェクトと厳密に同じ型の引数を1つだけ取る必要がありました。例えば、*int 型のオブジェクトにファイナライザを設定する場合、ファイナライザ関数も func(*int) のように *int 型の引数を取る必要がありました。

しかし、この厳密な型制約は、異なる型のオブジェクトに対して共通のファイナライザロジックを適用したい場合に不便でした。例えば、複数の異なる構造体に対して、共通のクリーンアップ処理を行いたい場合、それぞれの型に対応するファイナライザ関数を個別に定義する必要がありました。

このコミットは、この制約を緩和し、ファイナライザ関数が func(interface{}) 型の引数を受け入れられるようにすることで、より柔軟なファイナライザの定義を可能にすることを目的としています。これにより、ファイナライザ内で interface{} 型の引数を型アサーションすることで、様々な型のオブジェクトに対応できるようになります。

コミットメッセージにある Fixes #5368 は、この変更がGoのIssueトラッカーにおける特定の課題を解決したことを示唆しています。Web検索では直接的なIssue #5368の詳細は見つかりませんでしたが、runtime.SetFinalizer の柔軟性に関する議論はGoコミュニティで度々行われており、この変更はそのような背景から生まれたものと考えられます。

前提知識の解説

Go言語のガベージコレクション (GC)

Go言語は、自動メモリ管理のためにガベージコレクタ (GC) を採用しています。開発者は手動でメモリを解放する必要がなく、GCが不要になったメモリを自動的に回収します。GoのGCは並行・並行型で、プログラムの実行と同時に動作し、アプリケーションの停止時間を最小限に抑えるように設計されています。

runtime.SetFinalizer

runtime.SetFinalizer(obj, finalizer) は、Goの組み込み関数で、obj がガベージコレクタによって到達不能と判断された際に finalizer 関数を実行するように登録します。

  • obj: ファイナライザを設定するオブジェクトへのポインタ。new で割り当てられたオブジェクト、または複合リテラルのアドレスである必要があります。
  • finalizer: obj が到達不能になったときに実行される関数。この関数は obj と同じ型の引数を1つ取ります。

ファイナライザは、主にファイルディスクリプタ、ネットワーク接続、C言語のライブラリによって割り当てられたメモリなど、GoのGCが直接管理できない外部リソースのクリーンアップに使用されます。

注意点:

  • ファイナライザの実行タイミングは保証されません。GCの実行に依存するため、プログラム終了前に必ず実行されるとは限りません。
  • ファイナライザは別のゴルーチンで実行されます。
  • ファイナライザ内でオブジェクトへの参照を保持すると、そのオブジェクトのGCが遅延する可能性があります。
  • 1つのオブジェクトには1つのファイナライザしか設定できません。

interface{} (空インターフェース)

Go言語における interface{} は「空インターフェース」と呼ばれ、メソッドを一切持たないインターフェースです。Goのすべての型は、少なくとも0個のメソッドを実装しているため、interface{} はGoのあらゆる型の値を保持できます。これは、他の言語における Object 型や Any 型に似ていますが、Goのインターフェースは静的な型チェックの恩恵を受けつつ、動的な型を扱うための強力なメカニズムを提供します。

interface{} 型の変数に格納された値は、実行時に型アサーション (value.(Type)) を使用して元の型に変換できます。これにより、異なる型のデータを統一的に扱うことが可能になります。

Goの型システムとランタイム型情報

Goのランタイムは、実行時に型の情報を保持しています。これは、リフレクションやインターフェースの動的な挙動を可能にするために不可欠です。Type 構造体は型のメタデータを表し、Kind フィールドは型の種類(例えば、KindFunc は関数型、KindInterface はインターフェース型)を示します。FuncType は関数型の詳細(引数、戻り値など)を、InterfaceType はインターフェース型の詳細を保持します。

技術的詳細

このコミットの主要な変更点は、runtime.SetFinalizer がファイナライザ関数の引数として interface{} 型を受け入れられるように、ランタイムの型チェックロジックを修正したことです。

以前の runtime.SetFinalizer の実装では、ファイナライザ関数の唯一の引数の型が、ファイナライズされるオブジェクトの型と厳密に一致することを要求していました。このコミットでは、この型チェックに interface{} 型の引数も許容する条件が追加されました。

具体的には、以下の変更が行われています。

  1. runtime.SetFinalizer の型チェックロジックの変更 (src/pkg/runtime/malloc.goc):

    • ファイナライザ関数の引数が1つであること (ft->in.len != 1) は引き続きチェックされます。
    • 以前は *(Type**)ft->in.array != obj.type という条件で、ファイナライザの引数型がオブジェクトの型と一致しない場合にエラーとしていました。
    • このコミットでは、この条件が以下のように変更されました:
      fint = *(Type**)ft->in.array;
      if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0)
          ot = (PtrType*)obj.type;
      else if(fint != obj.type)
          goto badfunc;
      
      • fint はファイナライザ関数の引数型です。
      • fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0 の条件は、fint が空インターフェース (interface{}) であることをチェックしています。mhdr.len == 0 は、インターフェースがメソッドを持たないことを意味します。
      • もし引数型が interface{} であれば、ot (original type) にオブジェクトの型 (obj.type) を設定します。これは、ファイナライザが interface{} を受け取る場合でも、元のオブジェクトの型情報を保持しておく必要があるためです。
      • それ以外の場合 (else if(fint != obj.type)) は、以前と同様にファイナライザの引数型がオブジェクトの型と一致しない場合に badfunc へジャンプし、エラーとします。
    • この変更により、ファイナライザ関数が func(interface{}) の形式であっても SetFinalizer が成功するようになります。
  2. ファイナライザ情報の拡張 (src/pkg/runtime/mfinal.c, src/pkg/runtime/malloc.h, src/pkg/runtime/mgc0.c):

    • ファイナライザの情報を保持する struct Finvoid *ot; (original type) フィールドが追加されました。これは、ファイナライザが interface{} を受け取る場合に、元のオブジェクトの型情報を保存するために使用されます。
    • addfintab 関数や runtime·addfinalizer 関数など、ファイナライザを管理する内部関数に ot 引数が追加され、この情報がファイナライザテーブルに保存されるようになりました。
    • runtime·getfinalizer 関数も ot を返すように変更されました。
    • mgc0.chandlespecial 関数や runfinq 関数では、ファイナライザが実行される際に、この ot 情報を使用して interface{} 型の引数を正しく構築するロジックが追加されました。特に runfinq では、ファイナライザに渡すフレームのサイズ計算が sizeof(Eface) に変更され、Eface 構造体(インターフェースの内部表現)を構築してファイナライザに渡す処理が追加されています。
  3. runtime.SetFinalizer のドキュメント更新 (src/pkg/runtime/extern.go):

    • SetFinalizer のコメントが更新され、ファイナライザ関数が x の型、または interface{} 型の引数を受け入れられることが明記されました。
  4. テストケースの追加 (src/pkg/runtime/mfinal_test.go):

    • TestFinalizerTypeSucceed は、従来の func(*int) のような厳密な型を持つファイナライザが引き続き機能することを確認します。
    • TestFinalizerInterface は、func(interface{}) 型のファイナライザが *int 型のオブジェクトに対して正しく動作することを確認します。ファイナライザ内で interface{} から *int への型アサーションが行われ、値が正しいことが検証されます。
    • TestFinalizerInterfaceBig は、より大きな構造体 (bigValue) に対して func(interface{}) 型のファイナライザが正しく動作することを確認し、データが破損していないことを検証します。

これらの変更により、runtime.SetFinalizer はより柔軟になり、開発者は interface{} を利用して汎用的なファイナライザロジックを記述できるようになりました。

コアとなるコードの変更箇所

src/pkg/runtime/extern.go

  • SetFinalizer 関数のコメントが更新され、ファイナライザが func(interface{}) も受け入れることが明記されました。
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -122,8 +122,9 @@ func funcentry_go(*Func) uintptr
 // The argument x must be a pointer to an object allocated by
 // calling new or by taking the address of a composite literal.
 // The argument f must be a function that takes a single argument
-// of x's type and can have arbitrary ignored return values.
-// If either of these is not true, SetFinalizer aborts the program.
+// of x's type or interface{}, and can have arbitrary ignored return
+// values. If either of these is not true, SetFinalizer aborts the
+// program.
 //
 // Finalizers are run in dependency order: if A points at B, both have
 // finalizers, and they are otherwise unreachable, only the finalizer

src/pkg/runtime/malloc.goc

  • SetFinalizer 関数の内部実装で、ファイナライザ関数の引数型チェックロジックが変更されました。
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -813,11 +815,17 @@ func SetFinalizer(obj Eface, finalizer Eface) {
 		goto throw;
 	}
 	nret = 0;
+	ot = nil;
 	if(finalizer.type != nil) {
 		if(finalizer.type->kind != KindFunc)
 			goto badfunc;
 		ft = (FuncType*)finalizer.type;
-		if(ft->dotdotdot || ft->in.len != 1 || *(Type**)ft->in.array != obj.type)
+		if(ft->dotdotdot || ft->in.len != 1)
+			goto badfunc;
+		fint = *(Type**)ft->in.array;
+		if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0)
+			ot = (PtrType*)obj.type;
+		else if(fint != obj.type)
 			goto badfunc;
 
 		// compute size needed for return parameters
@@ -828,14 +836,14 @@ func SetFinalizer(obj Eface, finalizer Eface) {
 		nret = ROUND(nret, sizeof(void*));
 	}
 	
-	if(!runtime·addfinalizer(obj.data, finalizer.data, nret)) {
+	if(!runtime·addfinalizer(obj.data, finalizer.data, nret, ot)) {
 		runtime·printf("runtime.SetFinalizer: finalizer already set\n");
 		goto throw;
 	}
 	return;
 
 badfunc:
-	runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S)\\n", *finalizer.type->string, *obj.type->string);
+	runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S) or func(interface{})\\n", *finalizer.type->string, *obj.type->string);
 throw:
 	runtime·throw("runtime.SetFinalizer");
 }

src/pkg/runtime/malloc.h

  • runtime·getfinalizerruntime·addfinalizer 関数のシグネチャが変更され、void* 型の引数 ot が追加されました。
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -480,7 +480,7 @@ int32	runtime·gcprocs(void);
 void	runtime·helpgc(int32 nproc);
 void	runtime·gchelper(void);
 
-bool	runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret);
+bool	runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot);
 void	runtime·walkfintab(void (*fn)(void*));
 
 enum

src/pkg/runtime/mfinal.c

  • struct Finvoid *ot; フィールドが追加されました。
  • addfintabruntime·addfinalizerruntime·getfinalizer 関数が ot 引数を受け入れるように変更されました。
--- a/src/pkg/runtime/mfinal.c
+++ b/src/pkg/runtime/mfinal.c
@@ -13,6 +13,7 @@ struct Fin
 {
 	FuncVal *fn;
 	uintptr nret;
+	void *ot;
 };
 
 // Finalizer hash table.  Direct hash, linear scan, at most 3/4 full.
@@ -42,7 +43,7 @@ static struct {
 } fintab[TABSZ];
 
 static void
-addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret)
+addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret, void *ot)
 {
 	int32 i, j;
 
@@ -67,6 +68,7 @@ ret:
 	t->key[i] = k;
 	t->val[i].fn = fn;
 	t->val[i].nret = nret;
+	t->val[i].ot = ot;
 }
 
 static bool
@@ -87,6 +89,7 @@ lookfintab(Fintab *t, void *k, bool del, Fin *f)
 			t->key[i] = (void*)-1;
 			t->val[i].fn = nil;
 			t->val[i].nret = 0;
+			t->val[i].ot = nil;
 			t->ndead++;
 		}
 		return true;
@@ -123,7 +126,7 @@ resizefintab(Fintab *tab)
 	for(i=0; i<tab->max; i++) {
 		k = tab->key[i];
 		if(k != nil && k != (void*)-1)
-			addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret);
+			addfintab(&newtab, k, tab->val[i].fn, tab->val[i].nret, tab->val[i].ot);
 	}
 	
 	runtime·free(tab->key);
@@ -137,7 +140,7 @@ resizefintab(Fintab *tab)
 }
 
 bool
-runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
+runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, void *ot)
 {
 	Fintab *tab;
 	byte *base;
@@ -166,7 +169,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
 		resizefintab(tab);
 	}
 
-	addfintab(tab, p, f, nret);
+	addfintab(tab, p, f, nret, ot);
 	runtime·setblockspecial(p, true);
 	runtime·unlock(tab);
 	return true;
@@ -175,7 +178,7 @@ runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
 // get finalizer; if del, delete finalizer.
 // caller is responsible for updating RefHasFinalizer (special) bit.
 bool
-runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
+runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret, void **ot)
 {
 	Fintab *tab;
 	bool res;
@@ -189,6 +192,7 @@ runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
 		return false;
 	*fn = f.fn;
 	*nret = f.nret;
+	*ot = f.ot;
 	return true;
 }
 

src/pkg/runtime/mfinal_test.go

  • TestFinalizerTypeSucceed, TestFinalizerInterface, TestFinalizerInterfaceBig の3つのテストケースが追加されました。
--- a/src/pkg/runtime/mfinal_test.go
+++ b/src/pkg/runtime/mfinal_test.go
@@ -9,8 +9,94 @@ import (
 	"sync"
 	"sync/atomic"
 	"testing"
+	"time"
 )
 
+func TestFinalizerTypeSucceed(t *testing.T) {
+	if runtime.GOARCH != "amd64" {
+		t.Skipf("Skipping on non-amd64 machine")
+	}
+	ch := make(chan bool)
+	func() {
+		v := new(int)
+		*v = 97531
+		runtime.SetFinalizer(v, func(v *int) {
+			if *v != 97531 {
+				t.Errorf("*int in finalizer has the wrong value: %d\n", *v)
+			}
+			close(ch)
+		})
+		v = nil
+	}()
+	runtime.GC()
+	select {
+	case <-ch:
+	case <-time.After(time.Second * 4):
+		t.Errorf("Finalizer set by SetFinalizer(*int, func(*int)) didn't run")
+	}
+}
+
+func TestFinalizerInterface(t *testing.T) {
+	if runtime.GOARCH != "amd64" {
+		t.Skipf("Skipping on non-amd64 machine")
+	}
+	ch := make(chan bool)
+	func() {
+		v := new(int)
+		*v = 97531
+		runtime.SetFinalizer(v, func(v interface{}) {
+			i, ok := v.(*int)
+			if !ok {
+				t.Errorf("Expected *int from interface{} in finalizer, got %v", *i)
+			}
+			if *i != 97531 {
+				t.Errorf("*int from interface{} has the wrong value: %d\n", *i)
+			}
+			close(ch)
+		})
+		v = nil
+	}()
+	runtime.GC()
+	select {
+	case <-ch:
+	case <-time.After(time.Second * 4):
+		t.Errorf("Finalizer set by SetFinalizer(*int, func(interface{})) didn't run")
+	}
+}
+
+type bigValue struct {
+	fill uint64
+	it   bool
+	up   string
+}
+
+func TestFinalizerInterfaceBig(t *testing.T) {
+	if runtime.GOARCH != "amd64" {
+		t.Skipf("Skipping on non-amd64 machine")
+	}
+	ch := make(chan bool)
+	func() {
+		v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
+		runtime.SetFinalizer(v, func(v interface{}) {
+			i, ok := v.(*bigValue)
+			if !ok {
+				t.Errorf("Expected *bigValue from interface{} in finalizer, got %v", *i)
+			}
+			if i.fill != 0xDEADBEEFDEADBEEF && i.it != true && i.up != "It matters not how strait the gate" {
+				t.Errorf("*bigValue from interface{} has the wrong value: %d\n", *i)
+			}
+			close(ch)
+		})
+		v = nil
+	}()
+	runtime.GC()
+	select {
+	case <-ch:
+	case <-time.After(time.Second * 4):
+		t.Errorf("Finalizer set by SetFinalizer(*bigValue, func(interface{})) didn't run")
+	}
+}
+
 func fin(v *int) {
 }
 

src/pkg/runtime/mgc0.c

  • struct FinalizerPtrType *ot; フィールドが追加されました。
  • handlespecial 関数で runtime·getfinalizer の呼び出しに &ot が追加されました。
  • runfinq 関数で、ファイナライザに渡す引数の構築ロジックが変更され、interface{} 型の引数に対応する処理が追加されました。
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -109,6 +109,7 @@ struct Finalizer
 	FuncVal *fn;
 	void *arg;
 	uintptr nret;
+	PtrType *ot;
 };
 
 typedef struct FinBlock FinBlock;
@@ -1583,10 +1584,11 @@ handlespecial(byte *p, uintptr size)
 {
 	FuncVal *fn;
 	uintptr nret;
+	PtrType *ot;
 	FinBlock *block;
 	Finalizer *f;
 
-\tif(!runtime·getfinalizer(p, true, &fn, &nret)) {
+\tif(!runtime·getfinalizer(p, true, &fn, &nret, &ot)) {
 		runtime·setblockspecial(p, false);
 		runtime·MProf_Free(p, size);
 		return false;
@@ -1609,6 +1611,7 @@ handlespecial(byte *p, uintptr size)
 	finq->cnt++;
 	f->fn = fn;
 	f->nret = nret;
+	f->ot = ot;
 	f->arg = p;
 	runtime·unlock(&finlock);
 	return true;
@@ -2272,6 +2275,7 @@ runfinq(void)
 	FinBlock *fb, *next;
 	byte *frame;
 	uint32 framesz, framecap, i;
+	Eface *ef;
 
 	frame = nil;
 	framecap = 0;
@@ -2291,7 +2295,7 @@ runfinq(void)
 			next = fb->next;
 			for(i=0; i<fb->cnt; i++) {
 				f = &fb->fin[i];
-				framesz = sizeof(uintptr) + f->nret;
+				framesz = sizeof(Eface) + f->nret;
 				if(framecap < framesz) {
 					runtime·free(frame);
 					// The frame does not contain pointers interesting for GC,
@@ -2301,10 +2305,17 @@ runfinq(void)
 					frame = runtime·mallocgc(framesz, 0, FlagNoPointers|FlagNoInvokeGC);
 					framecap = framesz;
 				}
-				*(void**)frame = f->arg;
-				reflect·call(f->fn, frame, sizeof(uintptr) + f->nret);
+				if(f->ot == nil)
+					*(void**)frame = f->arg;
+				else {
+					ef = (Eface*)frame;
+					ef->type = f->ot;
+					ef->data = f->arg;
+				}
+				reflect·call(f->fn, frame, framesz);
 				f->fn = nil;
 				f->arg = nil;
+				f->ot = nil;
 			}
 			fb->cnt = 0;
 			fb->next = finc;

src/pkg/runtime/runtime.h

  • runtime·addfinalizer 関数のシグネチャが変更され、void* 型の引数 ot が追加されました。
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -808,7 +808,7 @@ uintptr	runtime·ifacehash(Iface, uintptr);\n uintptr	runtime·efacehash(Eface, uintptr);\n void*	runtime·malloc(uintptr size);\n void	runtime·free(void *v);\n-bool	runtime·addfinalizer(void*, FuncVal *fn, uintptr);\n+bool	runtime·addfinalizer(void*, FuncVal *fn, uintptr, void*);\n void	runtime·runpanic(Panic*);\n uintptr	runtime·getcallersp(void*);\n int32	runtime·mcount(void);\n```

## コアとなるコードの解説

このコミットの核心は、`runtime.SetFinalizer` が `func(interface{})` を受け入れられるようにするための、ランタイム内部の型チェックとファイナライザ実行ロジックの変更です。

1.  **`malloc.goc` における型チェックの緩和**:
    - 以前は、ファイナライザ関数の引数型が `obj.type` と完全に一致する必要がありました。
    - 新しいロジックでは、ファイナライザ関数の引数型 (`fint`) が `KindInterface` であり、かつメソッドを持たない (`mhdr.len == 0`)、つまり `interface{}` である場合に、特別な処理を行います。この場合、`ot` (original type) 変数に `obj.type` を格納します。これは、ファイナライザが `interface{}` を受け取る場合でも、元のオブジェクトの具体的な型情報を保持しておく必要があるためです。
    - これにより、`func(interface{})` が有効なファイナライザとして認識されるようになります。

2.  **`mfinal.c` におけるファイナライザ情報の拡張**:
    - `struct Fin` は、登録されたファイナライザの情報を保持する構造体です。ここに `void *ot;` フィールドが追加されました。この `ot` は、ファイナライザが `interface{}` を受け取る場合に、元のオブジェクトの具体的な型情報(`obj.type`)を指すポインタとして使用されます。
    - `addfintab` や `runtime·addfinalizer` といったファイナライザの登録・管理を行う関数群も、この `ot` を引数として受け取り、`struct Fin` に保存するように変更されました。これにより、ファイナライザが実行される際に、元のオブジェクトの型情報を参照できるようになります。

3.  **`mgc0.c` におけるファイナライザ実行時の引数構築**:
    - `runfinq` 関数は、ガベージコレクション後にファイナライザを実行する役割を担っています。
    - 以前は、ファイナライザに渡す引数フレームは単純にオブジェクトのデータポインタ (`f->arg`) を格納していました。
    - 新しいロジックでは、`f->ot` (original type) が `nil` でない場合(つまり、ファイナライザが `interface{}` を受け取るように登録された場合)、`Eface` 構造体(Goのインターフェースの内部表現)を構築します。
        - `ef->type = f->ot;`:`Eface` の型情報部分に、保存しておいた元のオブジェクトの型 (`ot`) を設定します。
        - `ef->data = f->arg;`:`Eface` のデータ部分に、元のオブジェクトのデータポインタ (`f->arg`) を設定します。
    - このようにして構築された `Eface` 構造体が、ファイナライザ関数の `interface{}` 引数として渡されます。これにより、ファイナライザ内で `v.(Type)` のように型アサーションを行うことで、元のオブジェクトの具体的な型にアクセスできるようになります。
    - ファイナライザに渡すフレームのサイズも、`sizeof(uintptr)` から `sizeof(Eface)` に変更され、インターフェースの内部表現を格納できる十分な領域が確保されるようになりました。

これらの変更により、Goランタイムは `runtime.SetFinalizer` に対して `func(interface{})` を受け入れるための内部的なメカニズムを確立し、より柔軟なファイナライザの利用を可能にしました。

## 関連リンク

- Go CL 11858043: [https://golang.org/cl/11858043](https://golang.org/cl/11858043)
- GitHubコミット: [https://github.com/golang/go/commit/6350e45892b5b0189fe3461ba1e7f530da23ff8f](https://github.com/golang/go/commit/6350e45892b5b0189fe3461ba1e7f530da23ff8f)

## 参考にした情報源リンク

- `runtime.SetFinalizer` のドキュメントと考慮事項:
    - [https://go.dev/pkg/runtime/#SetFinalizer](https://go.dev/pkg/runtime/#SetFinalizer)
    - [https://victoriametrics.com/blog/go-finalizers/](https://victoriametrics.com/blog/go-finalizers/)
- Goのガベージコレクションに関する情報:
    - [https://github.com/golang/go/issues/68398](https://github.com/golang/go/issues/68398)
    - [https://github.com/golang/go/issues/45581](https://github.com/golang/go/issues/45581)
- Go 1.24で導入された `runtime.AddCleanup` について (SetFinalizerの代替):
    - [https://go.dev/blog/go1.24-cleanup](https://go.dev/blog/go1.24-cleanup)
    - [https://github.com/golang/go/issues/63987](https://github.com/golang/go/issues/63987)
- Goのインターフェースの内部表現に関する情報:
    - [https://go.dev/blog/laws-of-reflection](https://go.dev/blog/laws-of-reflection)
    - [https://research.swtch.com/interfaces](https://research.swtch.com/interfaces)