PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
源码索引 / CURRENT IMPLEMENTATION

prepared.go

127 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。

跳到函数 / 声明
package spine

import (
	"context"
	"errors"
	"fmt"

	"github.com/tetratelabs/wazero"
	"github.com/tetratelabs/wazero/api"
	"github.com/tetratelabs/wazero/experimental"
)

// NewPrepared captures only our Guest's clean init, before any user code or tool.
// Each Run still has private allocated memory: this is full-copy, NOT page COW.
// This is not a generic Wasm checkpoint (Host resources/globals/tables aren't captured).
func NewPrepared(ctx context.Context, wasm []byte, manifest Manifest) (*Runner, error) {
	r, err := New(ctx, wasm, manifest)
	if err != nil {
		return nil, err
	}
	m, err := r.newGuest(ctx, &boundedText{}, &boundedText{})
	if err != nil {
		r.Close(context.Background())
		return nil, err
	}
	defer m.Close(context.Background())
	memory := m.Memory()
	data, ok := memory.Read(0, memory.Size())
	if !ok || len(data) == 0 {
		r.Close(context.Background())
		return nil, errors.New("cannot capture initialized Guest memory")
	}
	r.image = append([]byte(nil), data...)
	return r, nil
}

// NewPreparedCOW captures the initialized Guest in a sealed Linux memfd and
// attaches each fresh instance with MAP_PRIVATE at the original address. It
// shares only linear-memory pages; globals, tables, runtime objects and Host
// resources remain per-instance and are not part of this image.
func NewPreparedCOW(ctx context.Context, wasm []byte, manifest Manifest) (*Runner, error) {
	r, err := New(ctx, wasm, manifest)
	if err != nil {
		return nil, err
	}
	cow, err := newCOWRuntime()
	if err != nil {
		_ = r.Close(context.Background())
		return nil, err
	}
	r.cow = cow
	m, err := r.newGuest(ctx, &boundedText{}, &boundedText{})
	if err != nil {
		_ = r.Close(context.Background())
		return nil, err
	}
	if err := cow.capture(m.Memory()); err != nil {
		_ = m.Close(context.Background())
		_ = r.Close(context.Background())
		return nil, err
	}
	if err := m.Close(context.Background()); err != nil {
		_ = r.Close(context.Background())
		return nil, err
	}
	return r, nil
}

// All constructors use this lifecycle. Only CPython init vs memory restore differs.
func (r *Runner) newGuest(ctx context.Context, stdout, stderr *boundedText) (api.Module, error) {
	instantiateCtx := ctx
	var mapped cowMemory
	if r.cow != nil {
		var err error
		mapped, err = r.cow.allocator(r.code.ExportedMemories()["memory"])
		if err != nil {
			return nil, err
		}
		instantiateCtx = experimental.WithMemoryAllocator(instantiateCtx, mapped)
	}
	m, err := r.runtime.InstantiateModule(instantiateCtx, r.code, wazero.NewModuleConfig().WithName("").WithStartFunctions().WithStdout(stdout).WithStderr(stderr))
	if err != nil {
		if mapped != nil {
			mapped.Free() // Instantiation may fail before wazero takes ownership.
		}
		return nil, err
	}
	failed := true
	defer func() {
		if failed {
			m.Close(context.Background())
		}
	}()
	if _, err = m.ExportedFunction("_initialize").Call(ctx); err != nil {
		return nil, fmt.Errorf("initialize Guest: %w%s", err, stderr.String())
	}
	if r.cow != nil && r.cow.ready() {
		if err := r.cow.attach(m.Memory()); err != nil {
			return nil, err
		}
	} else if r.image == nil {
		status, err := m.ExportedFunction("init").Call(ctx)
		if err != nil {
			return nil, fmt.Errorf("init CPython: %w%s", err, stderr.String())
		}
		if status[0] != 0 {
			return nil, fmt.Errorf("CPython initialization failed: %s", stderr.String())
		}
	} else {
		// Instantiate/_initialize write data segments; restore the image AFTER them.
		memory := m.Memory()
		size := memory.Size()
		if int(size) > len(r.image) {
			return nil, errors.New("prepared memory shape changed")
		}
		if int(size) < len(r.image) {
			if _, ok := memory.Grow(uint32(len(r.image)-int(size)) / 65536); !ok {
				return nil, errors.New("cannot grow memory to prepared image")
			}
		}
		if !memory.Write(0, r.image) {
			return nil, errors.New("cannot copy prepared memory")
		}
	}
	failed = false
	return m, nil
}