Compare commits

...

5 commits

Author SHA1 Message Date
Daenney f942a3a991
Merge 9acf9ddc75 into e3019eada4 2024-10-01 12:38:18 +02:00
dependabot[bot] e3019eada4
[chore]: Bump go.uber.org/automaxprocs from 1.5.3 to 1.6.0 (#3376)
Bumps [go.uber.org/automaxprocs](https://github.com/uber-go/automaxprocs) from 1.5.3 to 1.6.0.
- [Release notes](https://github.com/uber-go/automaxprocs/releases)
- [Changelog](https://github.com/uber-go/automaxprocs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/automaxprocs/compare/v1.5.3...v1.6.0)

---
updated-dependencies:
- dependency-name: go.uber.org/automaxprocs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 12:58:44 +02:00
dependabot[bot] 188d28f054
[chore]: Bump github.com/ncruces/go-sqlite3 from 0.18.3 to 0.18.4 (#3375)
Bumps [github.com/ncruces/go-sqlite3](https://github.com/ncruces/go-sqlite3) from 0.18.3 to 0.18.4.
- [Release notes](https://github.com/ncruces/go-sqlite3/releases)
- [Commits](https://github.com/ncruces/go-sqlite3/compare/v0.18.3...v0.18.4)

---
updated-dependencies:
- dependency-name: github.com/ncruces/go-sqlite3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 12:46:23 +02:00
tobi 43e443f9f3
[bugfix] Carry-over "PinnedAt" when refreshing status (#3373) 2024-09-29 14:46:52 +02:00
Daenney 9acf9ddc75 [docs] Set the right site_url
Though the entry point is docs.gotosocial.org, that's redirected by RTD
to docs.gotosocial.org/en/latest/ which is where the actual site is
served from. However, other URLs like docs.gotosocial.org/admin aren't
redirected to docs.gotosocial.org/en/latest/admin. They just 404.

Without us including the /en/latest/ all the generated og:img URLs as
well as the link rel=canonical result in URLs that all 404.

This means that currently the social cards aren't working well, but
indexing the docs site by search engines is probably also partially
broken, since our sitemap.xml is also pointing at things that don't
exist.
2024-07-02 16:30:18 +02:00
29 changed files with 260 additions and 200 deletions

4
go.mod
View file

@ -44,7 +44,7 @@ require (
github.com/miekg/dns v1.1.62
github.com/minio/minio-go/v7 v7.0.77
github.com/mitchellh/mapstructure v1.5.0
github.com/ncruces/go-sqlite3 v0.18.3
github.com/ncruces/go-sqlite3 v0.18.4
github.com/oklog/ulid v1.3.1
github.com/prometheus/client_golang v1.20.4
github.com/spf13/cobra v1.8.1
@ -72,7 +72,7 @@ require (
go.opentelemetry.io/otel/sdk v1.29.0
go.opentelemetry.io/otel/sdk/metric v1.29.0
go.opentelemetry.io/otel/trace v1.29.0
go.uber.org/automaxprocs v1.5.3
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.27.0
golang.org/x/image v0.20.0
golang.org/x/net v0.29.0

8
go.sum
View file

@ -434,8 +434,8 @@ github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-sqlite3 v0.18.3 h1:tyMa75uh7LcINcfo0WrzOvcTkfz8Hqu0TEPX+KVyes4=
github.com/ncruces/go-sqlite3 v0.18.3/go.mod h1:HAwOtA+cyEX3iN6YmkpQwfT4vMMgCB7rQRFUdOgEFik=
github.com/ncruces/go-sqlite3 v0.18.4 h1:Je8o3y33MDwPYY/Cacas8yCsuoUzpNY/AgoSlN2ekyE=
github.com/ncruces/go-sqlite3 v0.18.4/go.mod h1:4HLag13gq1k10s4dfGBhMfRVsssJRT9/5hYqVM9RUYo=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
@ -652,8 +652,8 @@ go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt3
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=

View file

@ -516,10 +516,12 @@ func (d *Dereferencer) enrichStatus(
latestStatus.ID = status.ID
}
// Carry-over values and set fetch time.
latestStatus.UpdatedAt = status.UpdatedAt
// Set latest fetch time and carry-
// over some values from "old" status.
latestStatus.FetchedAt = time.Now()
latestStatus.UpdatedAt = status.UpdatedAt
latestStatus.Local = status.Local
latestStatus.PinnedAt = status.PinnedAt
// Carry-over approvals. Remote instances might not yet
// serve statuses with the `approved_by` field, but we

View file

@ -1,5 +1,5 @@
site_name: GoToSocial Documentation
site_url: https://docs.gotosocial.org
site_url: https://docs.gotosocial.org/en/latest/
theme:
name: material
language: en

View file

@ -10,7 +10,7 @@ as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro
It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite,
and uses [wazero](https://wazero.io/) as the runtime.\
Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies [^1].
Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies.
### Getting started
@ -49,6 +49,8 @@ db.QueryRow(`SELECT sqlite_version()`).Scan(&version)
simplifies [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html).
- [`github.com/ncruces/go-sqlite3/ext/bloom`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/bloom)
provides a [Bloom filter](https://github.com/nalgeon/sqlean/issues/27#issuecomment-1002267134) virtual table.
- [`github.com/ncruces/go-sqlite3/ext/closure`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/closure)
provides a transitive closure virtual table.
- [`github.com/ncruces/go-sqlite3/ext/csv`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/csv)
reads [comma-separated values](https://sqlite.org/csv.html).
- [`github.com/ncruces/go-sqlite3/ext/fileio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/fileio)
@ -108,7 +110,7 @@ It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
[wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach) thorough testing.
Every commit is [tested](https://github.com/ncruces/go-sqlite3/wiki/Test-matrix) on
Linux (amd64/arm64/386/riscv64/s390x), macOS (amd64/arm64),
Linux (amd64/arm64/386/riscv64/ppc64le/s390x), macOS (amd64/arm64),
Windows (amd64), FreeBSD (amd64), OpenBSD (amd64), NetBSD (amd64),
illumos (amd64), and Solaris (amd64).
@ -128,7 +130,4 @@ The Wasm and VFS layers are also tested by running SQLite's
- [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite)
- [`crawshaw.io/sqlite`](https://pkg.go.dev/crawshaw.io/sqlite)
- [`github.com/mattn/go-sqlite3`](https://pkg.go.dev/github.com/mattn/go-sqlite3)
- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite)
[^1]: anything else you find in `go.mod` is either a test dependency,
or needed by one of the extensions.
- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite)

View file

@ -302,7 +302,7 @@ func (c *Conn) SoftHeapLimit(n int64) int64 {
return int64(c.call("sqlite3_soft_heap_limit64", uint64(n)))
}
// SoftHeapLimit imposes a hard limit on heap size.
// HardHeapLimit imposes a hard limit on heap size.
//
// https://sqlite.org/c3ref/hard_heap_limit64.html
func (c *Conn) HardHeapLimit(n int64) int64 {

View file

@ -578,8 +578,15 @@ type rows struct {
*stmt
names []string
types []string
nulls []bool
}
var (
// Ensure these interfaces are implemented:
_ driver.RowsColumnTypeDatabaseTypeName = &rows{}
_ driver.RowsColumnTypeNullable = &rows{}
)
func (r *rows) Close() error {
r.Stmt.ClearBindings()
return r.Stmt.Reset()
@ -596,6 +603,22 @@ func (r *rows) Columns() []string {
return r.names
}
func (r *rows) loadTypes() {
if r.nulls == nil {
count := r.Stmt.ColumnCount()
r.nulls = make([]bool, count)
r.types = make([]string, count)
for i := range r.nulls {
if col := r.Stmt.ColumnOriginName(i); col != "" {
r.types[i], _, r.nulls[i], _, _, _ = r.Stmt.Conn().TableColumnMetadata(
r.Stmt.ColumnDatabaseName(i),
r.Stmt.ColumnTableName(i),
col)
}
}
}
}
func (r *rows) declType(index int) string {
if r.types == nil {
count := r.Stmt.ColumnCount()
@ -608,7 +631,8 @@ func (r *rows) declType(index int) string {
}
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
decltype := r.declType(index)
r.loadTypes()
decltype := r.types[index]
if len := len(decltype); len > 0 && decltype[len-1] == ')' {
if i := strings.LastIndexByte(decltype, '('); i >= 0 {
decltype = decltype[:i]
@ -617,6 +641,14 @@ func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
return strings.TrimSpace(decltype)
}
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
r.loadTypes()
if r.nulls[index] {
return false, true
}
return true, false
}
func (r *rows) Next(dest []driver.Value) error {
old := r.Stmt.Conn().SetInterrupt(r.ctx)
defer r.Stmt.Conn().SetInterrupt(old)

View file

@ -1,4 +1,4 @@
//go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys
//go:build !(darwin || linux) || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys
package alloc

View file

@ -1,4 +1,4 @@
//go:build unix && (amd64 || arm64 || riscv64) && !(sqlite3_noshm || sqlite3_nosys)
//go:build unix && (amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys)
package util

View file

@ -1,4 +1,4 @@
//go:build !unix || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys
//go:build !unix || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys
package util

View file

@ -0,0 +1,12 @@
package util
type Set[E comparable] map[E]struct{}
func (s Set[E]) Add(v E) {
s[v] = struct{}{}
}
func (s Set[E]) Contains(v E) bool {
_, ok := s[v]
return ok
}

View file

@ -46,7 +46,7 @@ to check if your build supports file locking.
### Write-Ahead Logging
On 64-bit Unix, this module uses `mmap` to implement
On 64-bit little-endian Unix, this module uses `mmap` to implement
[shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index),
like SQLite.

View file

@ -75,19 +75,7 @@ func (f *vfsFile) Lock(lock LockLevel) error {
if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE {
panic(util.AssertErr())
}
reserved := f.lock == LOCK_RESERVED
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if f.lock < LOCK_PENDING {
// If we're already RESERVED, we can block indefinitely,
// since only incoming readers may briefly hold the PENDING lock.
if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK {
return rc
}
f.lock = LOCK_PENDING
}
// We are now PENDING, so we're just waiting for readers to leave.
// If we were RESERVED, we can block for a bit before invoking the busy handler.
if rc := osGetExclusiveLock(f.File, reserved /* block */); rc != _OK {
if rc := osGetExclusiveLock(f.File, &f.lock); rc != _OK {
return rc
}
f.lock = LOCK_EXCLUSIVE

View file

@ -4,31 +4,15 @@
import (
"os"
"time"
"golang.org/x/sys/unix"
)
func osUnlock(file *os.File, start, len int64) _ErrorCode {
if start == 0 && len == 0 {
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
if err != nil {
return _IOERR_UNLOCK
}
}
return _OK
}
func osLock(file *os.File, how int, def _ErrorCode) _ErrorCode {
err := unix.Flock(int(file.Fd()), how)
return osLockErrorCode(err, def)
}
func osReadLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode {
func osGetSharedLock(file *os.File) _ErrorCode {
return osLock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
}
func osWriteLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode {
func osGetReservedLock(file *os.File) _ErrorCode {
rc := osLock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
if rc == _BUSY {
// The documentation states the lock is upgraded by releasing the previous lock,
@ -38,3 +22,40 @@ func osWriteLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time
}
return rc
}
func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
if *state >= LOCK_RESERVED {
return _OK
}
return osGetReservedLock(file)
}
func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
rc := osLock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
if rc == _BUSY {
// The documentation states the lock is upgraded by releasing the previous lock,
// then acquiring the new lock.
// This is a race, so return IOERR_RDLOCK to ensure the transaction is aborted.
return _IOERR_RDLOCK
}
return _OK
}
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
if err != nil {
return _IOERR_UNLOCK
}
return _OK
}
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
// Test the RESERVED lock.
lock, rc := osTestLock(file, _RESERVED_BYTE, 1)
return lock == unix.F_WRLCK, rc
}
func osLock(file *os.File, how int, def _ErrorCode) _ErrorCode {
err := unix.Flock(int(file.Fd()), how)
return osLockErrorCode(err, def)
}

View file

@ -1,4 +1,4 @@
//go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys
//go:build (amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys
package vfs
@ -9,6 +9,7 @@
)
const (
// https://godbolt.org/z/1PcK5vea3
_F2FS_IOC_START_ATOMIC_WRITE = 62721
_F2FS_IOC_COMMIT_ATOMIC_WRITE = 62722
_F2FS_IOC_ABORT_ATOMIC_WRITE = 62725

59
vendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
//go:build (linux || darwin) && !(sqlite3_flock || sqlite3_nosys)
package vfs
import (
"os"
"time"
"golang.org/x/sys/unix"
)
func osGetSharedLock(file *os.File) _ErrorCode {
// Test the PENDING lock before acquiring a new SHARED lock.
if lock, _ := osTestLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK {
return _BUSY
}
// Acquire the SHARED lock.
return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
}
func osGetReservedLock(file *os.File) _ErrorCode {
// Acquire the RESERVED lock.
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
}
func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
if *state == LOCK_RESERVED {
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if rc := osWriteLock(file, _PENDING_BYTE, 1, -1); rc != _OK {
return rc
}
*state = LOCK_PENDING
}
// Acquire the EXCLUSIVE lock.
return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond)
}
func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
if state >= LOCK_EXCLUSIVE {
// Downgrade to a SHARED lock.
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
// notest // this should never happen
return _IOERR_RDLOCK
}
}
// Release the PENDING and RESERVED locks.
return osUnlock(file, _PENDING_BYTE, 2)
}
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
// Release all locks.
return osUnlock(file, 0, 0)
}
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
// Test the RESERVED lock.
lock, rc := osTestLock(file, _RESERVED_BYTE, 1)
return lock == unix.F_WRLCK, rc
}

View file

@ -1,4 +1,4 @@
//go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys
//go:build !linux || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_nosys
package vfs

View file

@ -31,3 +31,41 @@ func osSetMode(file *os.File, modeof string) error {
}
return nil
}
func osTestLock(file *os.File, start, len int64) (int16, _ErrorCode) {
lock := unix.Flock_t{
Type: unix.F_WRLCK,
Start: start,
Len: len,
}
if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
return 0, _IOERR_CHECKRESERVEDLOCK
}
return lock.Type, _OK
}
func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
if err == nil {
return _OK
}
if errno, ok := err.(unix.Errno); ok {
switch errno {
case
unix.EACCES,
unix.EAGAIN,
unix.EBUSY,
unix.EINTR,
unix.ENOLCK,
unix.EDEADLK,
unix.ETIMEDOUT:
return _BUSY
case unix.EPERM:
return _PERM
}
// notest // usually EWOULDBLOCK == EAGAIN
if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
return _BUSY
}
}
return def
}

View file

@ -1,108 +0,0 @@
//go:build (linux || darwin || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
package vfs
import (
"os"
"time"
"golang.org/x/sys/unix"
)
func osGetSharedLock(file *os.File) _ErrorCode {
// Test the PENDING lock before acquiring a new SHARED lock.
if lock, _ := osGetLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK {
return _BUSY
}
// Acquire the SHARED lock.
return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
}
func osGetReservedLock(file *os.File) _ErrorCode {
// Acquire the RESERVED lock.
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
}
func osGetPendingLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration
if block {
timeout = -1
}
// Acquire the PENDING lock.
return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}
func osGetExclusiveLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration
if block {
timeout = time.Millisecond
}
// Acquire the EXCLUSIVE lock.
return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
}
func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
if state >= LOCK_EXCLUSIVE {
// Downgrade to a SHARED lock.
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
// In theory, the downgrade to a SHARED cannot fail because another
// process is holding an incompatible lock. If it does, this
// indicates that the other process is not following the locking
// protocol. If this happens, return IOERR_RDLOCK. Returning
// BUSY would confuse the upper layer.
// notest
return _IOERR_RDLOCK
}
}
// Release the PENDING and RESERVED locks.
return osUnlock(file, _PENDING_BYTE, 2)
}
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
// Release all locks.
return osUnlock(file, 0, 0)
}
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
// Test the RESERVED lock.
lock, rc := osGetLock(file, _RESERVED_BYTE, 1)
return lock == unix.F_WRLCK, rc
}
func osGetLock(file *os.File, start, len int64) (int16, _ErrorCode) {
lock := unix.Flock_t{
Type: unix.F_WRLCK,
Start: start,
Len: len,
}
if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
return 0, _IOERR_CHECKRESERVEDLOCK
}
return lock.Type, _OK
}
func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
if err == nil {
return _OK
}
if errno, ok := err.(unix.Errno); ok {
switch errno {
case
unix.EACCES,
unix.EAGAIN,
unix.EBUSY,
unix.EINTR,
unix.ENOLCK,
unix.EDEADLK,
unix.ETIMEDOUT:
return _BUSY
case unix.EPERM:
return _PERM
}
// notest // usually EWOULDBLOCK == EAGAIN
if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
return _BUSY
}
}
return def
}

View file

@ -28,27 +28,25 @@ func osGetReservedLock(file *os.File) _ErrorCode {
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
}
func osGetPendingLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration
if block {
timeout = -1
}
// Acquire the PENDING lock.
return osWriteLock(file, _PENDING_BYTE, 1, timeout)
}
func osGetExclusiveLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration
if block {
timeout = time.Millisecond
func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
// A PENDING lock is needed before releasing the SHARED lock.
if *state < LOCK_PENDING {
// If we were RESERVED, we can block indefinitely.
var timeout time.Duration
if *state == LOCK_RESERVED {
timeout = -1
}
if rc := osWriteLock(file, _PENDING_BYTE, 1, timeout); rc != _OK {
return rc
}
*state = LOCK_PENDING
}
// Release the SHARED lock.
osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
// Acquire the EXCLUSIVE lock.
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond)
if rc != _OK {
// Reacquire the SHARED lock.
@ -64,9 +62,7 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
// Reacquire the SHARED lock.
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
// This should never happen.
// We should always be able to reacquire the read lock.
// notest
// notest // this should never happen
return _IOERR_RDLOCK
}
}

View file

@ -1,4 +1,4 @@
//go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys)
//go:build (darwin || linux) && (amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys)
package vfs
@ -70,7 +70,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
}
// Dead man's switch.
if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK {
if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
return _IOERR_LOCK
} else if lock == unix.F_WRLCK {
return _BUSY

View file

@ -1,4 +1,4 @@
//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && (amd64 || arm64 || riscv64) && !(sqlite3_noshm || sqlite3_nosys)
//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && (amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys)
package vfs

View file

@ -1,4 +1,4 @@
//go:build !(darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys
//go:build !(darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys
package vfs

View file

@ -25,15 +25,18 @@
import (
"errors"
"math"
cg "go.uber.org/automaxprocs/internal/cgroups"
)
// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process
// to a valid GOMAXPROCS value.
func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) {
cgroups, err := newQueryer()
// to a valid GOMAXPROCS value. The quota is converted from float to int using round.
// If round == nil, DefaultRoundFunc is used.
func CPUQuotaToGOMAXPROCS(minValue int, round func(v float64) int) (int, CPUQuotaStatus, error) {
if round == nil {
round = DefaultRoundFunc
}
cgroups, err := _newQueryer()
if err != nil {
return -1, CPUQuotaUndefined, err
}
@ -43,7 +46,7 @@ func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) {
return -1, CPUQuotaUndefined, err
}
maxProcs := int(math.Floor(quota))
maxProcs := round(quota)
if minValue > 0 && maxProcs < minValue {
return minValue, CPUQuotaMinUsed, nil
}
@ -57,6 +60,7 @@ type queryer interface {
var (
_newCgroups2 = cg.NewCGroups2ForCurrentProcess
_newCgroups = cg.NewCGroupsForCurrentProcess
_newQueryer = newQueryer
)
func newQueryer() (queryer, error) {

View file

@ -26,6 +26,6 @@
// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process
// to a valid GOMAXPROCS value. This is Linux-specific and not supported in the
// current OS.
func CPUQuotaToGOMAXPROCS(_ int) (int, CPUQuotaStatus, error) {
func CPUQuotaToGOMAXPROCS(_ int, _ func(v float64) int) (int, CPUQuotaStatus, error) {
return -1, CPUQuotaUndefined, nil
}

View file

@ -20,6 +20,8 @@
package runtime
import "math"
// CPUQuotaStatus presents the status of how CPU quota is used
type CPUQuotaStatus int
@ -31,3 +33,8 @@
// CPUQuotaMinUsed is returned when CPU quota is smaller than the min value
CPUQuotaMinUsed
)
// DefaultRoundFunc is the default function to convert CPU quota from float to int. It rounds the value down (floor).
func DefaultRoundFunc(v float64) int {
return int(math.Floor(v))
}

View file

@ -37,9 +37,10 @@ func currentMaxProcs() int {
}
type config struct {
printf func(string, ...interface{})
procs func(int) (int, iruntime.CPUQuotaStatus, error)
minGOMAXPROCS int
printf func(string, ...interface{})
procs func(int, func(v float64) int) (int, iruntime.CPUQuotaStatus, error)
minGOMAXPROCS int
roundQuotaFunc func(v float64) int
}
func (c *config) log(fmt string, args ...interface{}) {
@ -71,6 +72,13 @@ func Min(n int) Option {
})
}
// RoundQuotaFunc sets the function that will be used to covert the CPU quota from float to int.
func RoundQuotaFunc(rf func(v float64) int) Option {
return optionFunc(func(cfg *config) {
cfg.roundQuotaFunc = rf
})
}
type optionFunc func(*config)
func (of optionFunc) apply(cfg *config) { of(cfg) }
@ -82,8 +90,9 @@ func (of optionFunc) apply(cfg *config) { of(cfg) }
// configured CPU quota.
func Set(opts ...Option) (func(), error) {
cfg := &config{
procs: iruntime.CPUQuotaToGOMAXPROCS,
minGOMAXPROCS: 1,
procs: iruntime.CPUQuotaToGOMAXPROCS,
roundQuotaFunc: iruntime.DefaultRoundFunc,
minGOMAXPROCS: 1,
}
for _, o := range opts {
o.apply(cfg)
@ -102,7 +111,7 @@ func Set(opts ...Option) (func(), error) {
return undoNoop, nil
}
maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS)
maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS, cfg.roundQuotaFunc)
if err != nil {
return undoNoop, err
}

View file

@ -21,4 +21,4 @@
package maxprocs
// Version is the current package version.
const Version = "1.5.2"
const Version = "1.6.0"

6
vendor/modules.txt vendored
View file

@ -517,7 +517,7 @@ github.com/modern-go/reflect2
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
## explicit
github.com/munnerz/goautoneg
# github.com/ncruces/go-sqlite3 v0.18.3
# github.com/ncruces/go-sqlite3 v0.18.4
## explicit; go 1.21
github.com/ncruces/go-sqlite3
github.com/ncruces/go-sqlite3/driver
@ -1046,8 +1046,8 @@ go.opentelemetry.io/proto/otlp/collector/trace/v1
go.opentelemetry.io/proto/otlp/common/v1
go.opentelemetry.io/proto/otlp/resource/v1
go.opentelemetry.io/proto/otlp/trace/v1
# go.uber.org/automaxprocs v1.5.3
## explicit; go 1.18
# go.uber.org/automaxprocs v1.6.0
## explicit; go 1.20
go.uber.org/automaxprocs/internal/cgroups
go.uber.org/automaxprocs/internal/runtime
go.uber.org/automaxprocs/maxprocs