Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SetAsyncBufferBudget ¶ added in v0.3.0
func SetAsyncBufferBudget(maxBytes int64)
SetAsyncBufferBudget sets the global cap (in bytes) on total read-ahead memory across all open buffers. 0 disables accounting (unlimited).
Types ¶
type AsyncReadBuffer ¶ added in v0.3.0
type AsyncReadBuffer struct {
// contains filtered or unexported fields
}
AsyncReadBuffer wraps a readAtContexter and reads ahead into a ring buffer so FUSE reads pull from pre-filled memory instead of blocking on the network-backed source. This mirrors the client-side read-ahead that rclone's VFS provides over the WebDAV endpoint, which the native FUSE mount otherwise lacks.
State machine (the key difference from the earlier reverted design, which reset and refilled on every non-sequential read and thus thrashed under Plex/Jellyfin seek bursts):
- probing: no buffer allocated, no fill goroutine work; every read is a direct passthrough to the source. Consecutive sequential reads are counted.
- streaming: after armThreshold sustained sequential reads, the buffer is allocated (subject to the global memory budget) and a single fill goroutine reads ahead. A non-sequential read (seek) demotes back to probing, freeing the buffer and budget; read-ahead only re-arms after sequential reads resume.
A single fill goroutine is started lazily on first promotion and parked on the cond while probing, so promote/demote cycles never spawn goroutines.
func NewAsyncReadBuffer ¶ added in v0.3.0
func NewAsyncReadBuffer(ctx context.Context, src readAtContexter, bufSize int, fileSize int64, log *slog.Logger) *AsyncReadBuffer
NewAsyncReadBuffer creates an async read-ahead buffer wrapping src. The fill goroutine is started lazily on first promotion, so opening a file that is only probed (header reads) and closed never allocates the buffer or spawns a goroutine.
func (*AsyncReadBuffer) Close ¶ added in v0.3.0
func (a *AsyncReadBuffer) Close()
Close stops the fill goroutine and releases resources in a single call. It is equivalent to Shutdown followed by Wait. Callers that own the underlying source should prefer Shutdown → close source → Wait so an in-flight source read is unblocked promptly; Close on its own relies on the bounded safety-net timeout when a source read is wedged.
func (*AsyncReadBuffer) GetBufferedOffset ¶ added in v0.3.0
func (a *AsyncReadBuffer) GetBufferedOffset() int64
GetBufferedOffset returns the file offset up to which data is currently buffered (baseOff+filled), or 0 when probing.
func (*AsyncReadBuffer) ReadAtContext ¶ added in v0.3.0
ReadAtContext serves a read at the given absolute offset. Sequential reads in streaming mode are served from the ring buffer; everything else is a direct passthrough to the source while the sequential-run counter decides whether to (re)arm read-ahead.
func (*AsyncReadBuffer) Shutdown ¶ added in v0.3.0
func (a *AsyncReadBuffer) Shutdown()
Shutdown signals the fill goroutine to stop without waiting for it to exit. It cancels the buffer's context and marks the buffer closed so the goroutine exits at its next lock acquisition without issuing another source read.
Shutdown does NOT close the underlying source — the FUSE handle owns that lifecycle. Because a sequential source read blocks on the source's own context (not this buffer's), the caller MUST close/interrupt the source between Shutdown and Wait; otherwise an in-flight read cannot be unblocked and Wait falls back to its bounded safety-net timeout. Idempotent.
func (*AsyncReadBuffer) Wait ¶ added in v0.3.0
func (a *AsyncReadBuffer) Wait()
Wait drains the fill goroutine and releases resources. It must be called after Shutdown (and after the underlying source has been closed/interrupted) so the goroutine exits promptly. The bounded wait is a safety net against a wedged source: with the source interrupted first it returns near-instantly. Idempotent.
type Backend ¶
type Backend interface {
// Mount starts the FUSE filesystem. Blocks until unmount.
// onReady is called once the kernel mount is confirmed live.
Mount(ctx context.Context, onReady func()) error
// Unmount gracefully unmounts the filesystem.
Unmount() error
// ForceUnmount attempts platform-specific force unmount.
ForceUnmount() error
// Type returns the backend type.
Type() Type
}
Backend abstracts FUSE mount/unmount operations.
type Config ¶
type Config struct {
MountPoint string
NzbFs *nzbfilesystem.NzbFilesystem
FuseConfig config.FuseConfig
StreamTracker StreamTracker
UID uint32
GID uint32
}
Config holds parameters common to all backends.
type Refresher ¶
type Refresher interface {
RefreshDirectory(name string)
}
Refresher is optionally implemented by backends that support kernel cache invalidation (e.g. hanwen via NotifyContent/NotifyEntry).
type StreamTracker ¶
type StreamTracker interface {
AddStream(filePath, source, userName, clientIP, userAgent string, totalSize int64) *nzbfilesystem.ActiveStream
UpdateProgress(id string, bytesRead int64)
Remove(id string)
}
StreamTracker is the subset of stream tracking needed by FUSE backends.
type Type ¶
type Type string
Type identifies a FUSE backend implementation.
func DefaultType ¶
func DefaultType() Type
DefaultType returns the platform-default backend type. Linux uses hanwen (pure Go). macOS uses cgo (Fuse-T). Windows FUSE support via WinFsp requires a native Windows build; cross-compiled Windows binaries default to cgo but FUSE mounting is not supported without WinFsp. Override with ALTMOUNT_FUSE_BACKEND env var (checked by caller).