mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
- Delete pkg/sentry/fs/*. - Move pkg/sentry/fs/fsutil out of VFS1 directory and remove VFS1 components. - Remove remaining unused references to VFS1 from remaining codebase. - Rename/refactor code to avoid even referencing VFS2, unless necessary. - Rewrite VFS1-only tests to VFS2. Updates #1624 PiperOrigin-RevId: 490064269
695 lines
17 KiB
Go
695 lines
17 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package semaphore implements System V semaphores.
|
|
package semaphore
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/ipc"
|
|
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
"gvisor.dev/gvisor/pkg/sync"
|
|
)
|
|
|
|
const (
|
|
// Maximum semaphore value.
|
|
valueMax = linux.SEMVMX
|
|
|
|
// Maximum number of semaphore sets.
|
|
setsMax = linux.SEMMNI
|
|
|
|
// Maximum number of semaphores in a semaphore set.
|
|
semsMax = linux.SEMMSL
|
|
|
|
// Maximum number of semaphores in all semaphore sets.
|
|
semsTotalMax = linux.SEMMNS
|
|
)
|
|
|
|
// Registry maintains a set of semaphores that can be found by key or ID.
|
|
//
|
|
// +stateify savable
|
|
type Registry struct {
|
|
// mu protects all fields below.
|
|
mu sync.Mutex `state:"nosave"`
|
|
|
|
// reg defines basic fields and operations needed for all SysV registries.
|
|
reg *ipc.Registry
|
|
|
|
// indexes maintains a mapping between a set's index in virtual array and
|
|
// its identifier.
|
|
indexes map[int32]ipc.ID
|
|
}
|
|
|
|
// Set represents a set of semaphores that can be operated atomically.
|
|
//
|
|
// +stateify savable
|
|
type Set struct {
|
|
// registry owning this sem set. Immutable.
|
|
registry *Registry
|
|
|
|
// mu protects all fields below.
|
|
mu sync.Mutex `state:"nosave"`
|
|
|
|
obj *ipc.Object
|
|
|
|
opTime ktime.Time
|
|
changeTime ktime.Time
|
|
|
|
// sems holds all semaphores in the set. The slice itself is immutable after
|
|
// it's been set, however each 'sem' object in the slice requires 'mu' lock.
|
|
sems []sem
|
|
|
|
// dead is set to true when the set is removed and can't be reached anymore.
|
|
// All waiters must wake up and fail when set is dead.
|
|
dead bool
|
|
}
|
|
|
|
// sem represents a single semaphore from a set.
|
|
//
|
|
// +stateify savable
|
|
type sem struct {
|
|
value int16
|
|
waiters waiterList `state:"zerovalue"`
|
|
pid int32
|
|
}
|
|
|
|
// waiter represents a caller that is waiting for the semaphore value to
|
|
// become positive or zero.
|
|
//
|
|
// +stateify savable
|
|
type waiter struct {
|
|
waiterEntry
|
|
|
|
// value represents how much resource the waiter needs to wake up.
|
|
// The value is either 0 or negative.
|
|
value int16
|
|
ch chan struct{}
|
|
}
|
|
|
|
// NewRegistry creates a new semaphore set registry.
|
|
func NewRegistry(userNS *auth.UserNamespace) *Registry {
|
|
return &Registry{
|
|
reg: ipc.NewRegistry(userNS),
|
|
indexes: make(map[int32]ipc.ID),
|
|
}
|
|
}
|
|
|
|
// FindOrCreate searches for a semaphore set that matches 'key'. If not found,
|
|
// it may create a new one if requested. If private is true, key is ignored and
|
|
// a new set is always created. If create is false, it fails if a set cannot
|
|
// be found. If exclusive is true, it fails if a set with the same key already
|
|
// exists.
|
|
func (r *Registry) FindOrCreate(ctx context.Context, key ipc.Key, nsems int32, mode linux.FileMode, private, create, exclusive bool) (*Set, error) {
|
|
if nsems < 0 || nsems > semsMax {
|
|
return nil, linuxerr.EINVAL
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if !private {
|
|
set, err := r.reg.Find(ctx, key, mode, create, exclusive)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Validate semaphore-specific parameters.
|
|
if set != nil {
|
|
set := set.(*Set)
|
|
if nsems > int32(set.Size()) {
|
|
return nil, linuxerr.EINVAL
|
|
}
|
|
return set, nil
|
|
}
|
|
}
|
|
|
|
// Zero is only valid if an existing set is found.
|
|
if nsems == 0 {
|
|
return nil, linuxerr.EINVAL
|
|
}
|
|
|
|
// Apply system limits.
|
|
//
|
|
// Map reg.objects and map indexes in a registry are of the same size,
|
|
// check map reg.objects only here for the system limit.
|
|
if r.reg.ObjectCount() >= setsMax {
|
|
return nil, linuxerr.ENOSPC
|
|
}
|
|
if r.totalSems() > int(semsTotalMax-nsems) {
|
|
return nil, linuxerr.ENOSPC
|
|
}
|
|
|
|
// Finally create a new set.
|
|
return r.newSetLocked(ctx, key, auth.CredentialsFromContext(ctx), mode, nsems)
|
|
}
|
|
|
|
// IPCInfo returns information about system-wide semaphore limits and parameters.
|
|
func (r *Registry) IPCInfo() *linux.SemInfo {
|
|
return &linux.SemInfo{
|
|
SemMap: linux.SEMMAP,
|
|
SemMni: linux.SEMMNI,
|
|
SemMns: linux.SEMMNS,
|
|
SemMnu: linux.SEMMNU,
|
|
SemMsl: linux.SEMMSL,
|
|
SemOpm: linux.SEMOPM,
|
|
SemUme: linux.SEMUME,
|
|
SemUsz: linux.SEMUSZ,
|
|
SemVmx: linux.SEMVMX,
|
|
SemAem: linux.SEMAEM,
|
|
}
|
|
}
|
|
|
|
// SemInfo returns a seminfo structure containing the same information as
|
|
// for IPC_INFO, except that SemUsz field returns the number of existing
|
|
// semaphore sets, and SemAem field returns the number of existing semaphores.
|
|
func (r *Registry) SemInfo() *linux.SemInfo {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
info := r.IPCInfo()
|
|
info.SemUsz = uint32(r.reg.ObjectCount())
|
|
info.SemAem = uint32(r.totalSems())
|
|
|
|
return info
|
|
}
|
|
|
|
// HighestIndex returns the index of the highest used entry in
|
|
// the kernel's array.
|
|
func (r *Registry) HighestIndex() int32 {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
// By default, highest used index is 0 even though
|
|
// there is no semaphore set.
|
|
var highestIndex int32
|
|
for index := range r.indexes {
|
|
if index > highestIndex {
|
|
highestIndex = index
|
|
}
|
|
}
|
|
return highestIndex
|
|
}
|
|
|
|
// Remove removes set with give 'id' from the registry and marks the set as
|
|
// dead. All waiters will be awakened and fail.
|
|
func (r *Registry) Remove(id ipc.ID, creds *auth.Credentials) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
index, found := r.findIndexByID(id)
|
|
if !found {
|
|
return linuxerr.EINVAL
|
|
}
|
|
delete(r.indexes, index)
|
|
|
|
r.reg.Remove(id, creds)
|
|
|
|
return nil
|
|
}
|
|
|
|
// newSetLocked creates a new Set using given fields. An error is returned if there
|
|
// are no more available identifiers.
|
|
//
|
|
// Precondition: r.mu must be held.
|
|
func (r *Registry) newSetLocked(ctx context.Context, key ipc.Key, creator *auth.Credentials, mode linux.FileMode, nsems int32) (*Set, error) {
|
|
set := &Set{
|
|
registry: r,
|
|
obj: ipc.NewObject(r.reg.UserNS, ipc.Key(key), creator, creator, mode),
|
|
changeTime: ktime.NowFromContext(ctx),
|
|
sems: make([]sem, nsems),
|
|
}
|
|
|
|
err := r.reg.Register(set)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
index, found := r.findFirstAvailableIndex()
|
|
if !found {
|
|
// See linux, ipc/sem.c:newary().
|
|
return nil, linuxerr.ENOSPC
|
|
}
|
|
r.indexes[index] = set.obj.ID
|
|
|
|
return set, nil
|
|
}
|
|
|
|
// FindByID looks up a set given an ID.
|
|
func (r *Registry) FindByID(id ipc.ID) *Set {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
mech := r.reg.FindByID(id)
|
|
if mech == nil {
|
|
return nil
|
|
}
|
|
return mech.(*Set)
|
|
}
|
|
|
|
// FindByIndex looks up a set given an index.
|
|
func (r *Registry) FindByIndex(index int32) *Set {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
id, present := r.indexes[index]
|
|
if !present {
|
|
return nil
|
|
}
|
|
return r.reg.FindByID(id).(*Set)
|
|
}
|
|
|
|
func (r *Registry) findIndexByID(id ipc.ID) (int32, bool) {
|
|
for k, v := range r.indexes {
|
|
if v == id {
|
|
return k, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (r *Registry) findFirstAvailableIndex() (int32, bool) {
|
|
for index := int32(0); index < setsMax; index++ {
|
|
if _, present := r.indexes[index]; !present {
|
|
return index, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (r *Registry) totalSems() int {
|
|
totalSems := 0
|
|
r.reg.ForAllObjects(
|
|
func(o ipc.Mechanism) {
|
|
totalSems += o.(*Set).Size()
|
|
},
|
|
)
|
|
return totalSems
|
|
}
|
|
|
|
// ID returns semaphore's ID.
|
|
func (s *Set) ID() ipc.ID {
|
|
return s.obj.ID
|
|
}
|
|
|
|
// Object implements ipc.Mechanism.Object.
|
|
func (s *Set) Object() *ipc.Object {
|
|
return s.obj
|
|
}
|
|
|
|
// Lock implements ipc.Mechanism.Lock.
|
|
func (s *Set) Lock() {
|
|
s.mu.Lock()
|
|
}
|
|
|
|
// Unlock implements ipc.mechanism.Unlock.
|
|
//
|
|
// +checklocksignore
|
|
func (s *Set) Unlock() {
|
|
s.mu.Unlock()
|
|
}
|
|
|
|
func (s *Set) findSem(num int32) *sem {
|
|
if num < 0 || int(num) >= s.Size() {
|
|
return nil
|
|
}
|
|
return &s.sems[num]
|
|
}
|
|
|
|
// Size returns the number of semaphores in the set. Size is immutable.
|
|
func (s *Set) Size() int {
|
|
return len(s.sems)
|
|
}
|
|
|
|
// Set modifies attributes for a semaphore set. See semctl(IPC_SET).
|
|
func (s *Set) Set(ctx context.Context, ds *linux.SemidDS) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if err := s.obj.Set(ctx, &ds.SemPerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.changeTime = ktime.NowFromContext(ctx)
|
|
return nil
|
|
}
|
|
|
|
// GetStat extracts semid_ds information from the set.
|
|
func (s *Set) GetStat(creds *auth.Credentials) (*linux.SemidDS, error) {
|
|
// "The calling process must have read permission on the semaphore set."
|
|
return s.semStat(creds, vfs.MayRead)
|
|
}
|
|
|
|
// GetStatAny extracts semid_ds information from the set without requiring read access.
|
|
func (s *Set) GetStatAny(creds *auth.Credentials) (*linux.SemidDS, error) {
|
|
return s.semStat(creds, 0)
|
|
}
|
|
|
|
func (s *Set) semStat(creds *auth.Credentials, ats vfs.AccessTypes) (*linux.SemidDS, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if !s.obj.CheckPermissions(creds, ats) {
|
|
return nil, linuxerr.EACCES
|
|
}
|
|
|
|
return &linux.SemidDS{
|
|
SemPerm: linux.IPCPerm{
|
|
Key: uint32(s.obj.Key),
|
|
UID: uint32(creds.UserNamespace.MapFromKUID(s.obj.OwnerUID)),
|
|
GID: uint32(creds.UserNamespace.MapFromKGID(s.obj.OwnerGID)),
|
|
CUID: uint32(creds.UserNamespace.MapFromKUID(s.obj.CreatorUID)),
|
|
CGID: uint32(creds.UserNamespace.MapFromKGID(s.obj.CreatorGID)),
|
|
Mode: uint16(s.obj.Mode),
|
|
Seq: 0, // IPC sequence not supported.
|
|
},
|
|
SemOTime: s.opTime.TimeT(),
|
|
SemCTime: s.changeTime.TimeT(),
|
|
SemNSems: uint64(s.Size()),
|
|
}, nil
|
|
}
|
|
|
|
// SetVal overrides a semaphore value, waking up waiters as needed.
|
|
func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Credentials, pid int32) error {
|
|
if val < 0 || val > valueMax {
|
|
return linuxerr.ERANGE
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// "The calling process must have alter permission on the semaphore set."
|
|
if !s.obj.CheckPermissions(creds, vfs.MayWrite) {
|
|
return linuxerr.EACCES
|
|
}
|
|
|
|
sem := s.findSem(num)
|
|
if sem == nil {
|
|
return linuxerr.ERANGE
|
|
}
|
|
|
|
// TODO(gvisor.dev/issue/137): Clear undo entries in all processes.
|
|
sem.value = val
|
|
sem.pid = pid
|
|
s.changeTime = ktime.NowFromContext(ctx)
|
|
sem.wakeWaiters()
|
|
return nil
|
|
}
|
|
|
|
// SetValAll overrides all semaphores values, waking up waiters as needed. It also
|
|
// sets semaphore's PID which was fixed in Linux 4.6.
|
|
//
|
|
// 'len(vals)' must be equal to 's.Size()'.
|
|
func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credentials, pid int32) error {
|
|
if len(vals) != s.Size() {
|
|
panic(fmt.Sprintf("vals length (%d) different that Set.Size() (%d)", len(vals), s.Size()))
|
|
}
|
|
|
|
for _, val := range vals {
|
|
if val > valueMax {
|
|
return linuxerr.ERANGE
|
|
}
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// "The calling process must have alter permission on the semaphore set."
|
|
if !s.obj.CheckPermissions(creds, vfs.MayWrite) {
|
|
return linuxerr.EACCES
|
|
}
|
|
|
|
for i, val := range vals {
|
|
sem := &s.sems[i]
|
|
|
|
// TODO(gvisor.dev/issue/137): Clear undo entries in all processes.
|
|
sem.value = int16(val)
|
|
sem.pid = pid
|
|
sem.wakeWaiters()
|
|
}
|
|
s.changeTime = ktime.NowFromContext(ctx)
|
|
return nil
|
|
}
|
|
|
|
// GetVal returns a semaphore value.
|
|
func (s *Set) GetVal(num int32, creds *auth.Credentials) (int16, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// "The calling process must have read permission on the semaphore set."
|
|
if !s.obj.CheckPermissions(creds, vfs.MayRead) {
|
|
return 0, linuxerr.EACCES
|
|
}
|
|
|
|
sem := s.findSem(num)
|
|
if sem == nil {
|
|
return 0, linuxerr.ERANGE
|
|
}
|
|
return sem.value, nil
|
|
}
|
|
|
|
// GetValAll returns value for all semaphores.
|
|
func (s *Set) GetValAll(creds *auth.Credentials) ([]uint16, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// "The calling process must have read permission on the semaphore set."
|
|
if !s.obj.CheckPermissions(creds, vfs.MayRead) {
|
|
return nil, linuxerr.EACCES
|
|
}
|
|
|
|
vals := make([]uint16, s.Size())
|
|
for i, sem := range s.sems {
|
|
vals[i] = uint16(sem.value)
|
|
}
|
|
return vals, nil
|
|
}
|
|
|
|
// GetPID returns the PID set when performing operations in the semaphore.
|
|
func (s *Set) GetPID(num int32, creds *auth.Credentials) (int32, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// "The calling process must have read permission on the semaphore set."
|
|
if !s.obj.CheckPermissions(creds, vfs.MayRead) {
|
|
return 0, linuxerr.EACCES
|
|
}
|
|
|
|
sem := s.findSem(num)
|
|
if sem == nil {
|
|
return 0, linuxerr.ERANGE
|
|
}
|
|
return sem.pid, nil
|
|
}
|
|
|
|
func (s *Set) countWaiters(num int32, creds *auth.Credentials, pred func(w *waiter) bool) (uint16, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// The calling process must have read permission on the semaphore set.
|
|
if !s.obj.CheckPermissions(creds, vfs.MayRead) {
|
|
return 0, linuxerr.EACCES
|
|
}
|
|
|
|
sem := s.findSem(num)
|
|
if sem == nil {
|
|
return 0, linuxerr.ERANGE
|
|
}
|
|
var cnt uint16
|
|
for w := sem.waiters.Front(); w != nil; w = w.Next() {
|
|
if pred(w) {
|
|
cnt++
|
|
}
|
|
}
|
|
return cnt, nil
|
|
}
|
|
|
|
// CountZeroWaiters returns number of waiters waiting for the sem's value to increase.
|
|
func (s *Set) CountZeroWaiters(num int32, creds *auth.Credentials) (uint16, error) {
|
|
return s.countWaiters(num, creds, func(w *waiter) bool {
|
|
return w.value == 0
|
|
})
|
|
}
|
|
|
|
// CountNegativeWaiters returns number of waiters waiting for the sem to go to zero.
|
|
func (s *Set) CountNegativeWaiters(num int32, creds *auth.Credentials) (uint16, error) {
|
|
return s.countWaiters(num, creds, func(w *waiter) bool {
|
|
return w.value < 0
|
|
})
|
|
}
|
|
|
|
// ExecuteOps attempts to execute a list of operations to the set. It only
|
|
// succeeds when all operations can be applied. No changes are made if it fails.
|
|
//
|
|
// On failure, it may return an error (retries are hopeless) or it may return
|
|
// a channel that can be waited on before attempting again.
|
|
func (s *Set) ExecuteOps(ctx context.Context, ops []linux.Sembuf, creds *auth.Credentials, pid int32) (chan struct{}, int32, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// Did it race with a removal operation?
|
|
if s.dead {
|
|
return nil, 0, linuxerr.EIDRM
|
|
}
|
|
|
|
// Validate the operations.
|
|
readOnly := true
|
|
for _, op := range ops {
|
|
if s.findSem(int32(op.SemNum)) == nil {
|
|
return nil, 0, linuxerr.EFBIG
|
|
}
|
|
if op.SemOp != 0 {
|
|
readOnly = false
|
|
}
|
|
}
|
|
|
|
ats := vfs.MayRead
|
|
if !readOnly {
|
|
ats = vfs.MayWrite
|
|
}
|
|
if !s.obj.CheckPermissions(creds, ats) {
|
|
return nil, 0, linuxerr.EACCES
|
|
}
|
|
|
|
ch, num, err := s.executeOps(ctx, ops, pid)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return ch, num, nil
|
|
}
|
|
|
|
func (s *Set) executeOps(ctx context.Context, ops []linux.Sembuf, pid int32) (chan struct{}, int32, error) {
|
|
// Changes to semaphores go to this slice temporarily until they all succeed.
|
|
tmpVals := make([]int16, len(s.sems))
|
|
for i := range s.sems {
|
|
tmpVals[i] = s.sems[i].value
|
|
}
|
|
|
|
for _, op := range ops {
|
|
sem := &s.sems[op.SemNum]
|
|
if op.SemOp == 0 {
|
|
// Handle 'wait for zero' operation.
|
|
if tmpVals[op.SemNum] != 0 {
|
|
// Semaphore isn't 0, must wait.
|
|
if op.SemFlg&linux.IPC_NOWAIT != 0 {
|
|
return nil, 0, linuxerr.ErrWouldBlock
|
|
}
|
|
|
|
w := newWaiter(op.SemOp)
|
|
sem.waiters.PushBack(w)
|
|
return w.ch, int32(op.SemNum), nil
|
|
}
|
|
} else {
|
|
if op.SemOp < 0 {
|
|
// Handle 'wait' operation.
|
|
if -op.SemOp > valueMax {
|
|
return nil, 0, linuxerr.ERANGE
|
|
}
|
|
if -op.SemOp > tmpVals[op.SemNum] {
|
|
// Not enough resources, must wait.
|
|
if op.SemFlg&linux.IPC_NOWAIT != 0 {
|
|
return nil, 0, linuxerr.ErrWouldBlock
|
|
}
|
|
|
|
w := newWaiter(op.SemOp)
|
|
sem.waiters.PushBack(w)
|
|
return w.ch, int32(op.SemNum), nil
|
|
}
|
|
} else {
|
|
// op.SemOp > 0: Handle 'signal' operation.
|
|
if tmpVals[op.SemNum] > valueMax-op.SemOp {
|
|
return nil, 0, linuxerr.ERANGE
|
|
}
|
|
}
|
|
|
|
tmpVals[op.SemNum] += op.SemOp
|
|
}
|
|
}
|
|
|
|
// All operations succeeded, apply them.
|
|
// TODO(gvisor.dev/issue/137): handle undo operations.
|
|
for i, v := range tmpVals {
|
|
s.sems[i].value = v
|
|
s.sems[i].wakeWaiters()
|
|
s.sems[i].pid = pid
|
|
}
|
|
s.opTime = ktime.NowFromContext(ctx)
|
|
return nil, 0, nil
|
|
}
|
|
|
|
// AbortWait notifies that a waiter is giving up and will not wait on the
|
|
// channel anymore.
|
|
func (s *Set) AbortWait(num int32, ch chan struct{}) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
sem := &s.sems[num]
|
|
for w := sem.waiters.Front(); w != nil; w = w.Next() {
|
|
if w.ch == ch {
|
|
sem.waiters.Remove(w)
|
|
return
|
|
}
|
|
}
|
|
// Waiter may not be found in case it raced with wakeWaiters().
|
|
}
|
|
|
|
// Destroy implements ipc.Mechanism.Destroy.
|
|
//
|
|
// Preconditions: Caller must hold 's.mu'.
|
|
func (s *Set) Destroy() {
|
|
// Notify all waiters. They will fail on the next attempt to execute
|
|
// operations and return error.
|
|
s.dead = true
|
|
for _, s := range s.sems {
|
|
for w := s.waiters.Front(); w != nil; w = w.Next() {
|
|
w.ch <- struct{}{}
|
|
}
|
|
s.waiters.Reset()
|
|
}
|
|
}
|
|
|
|
func abs(val int16) int16 {
|
|
if val < 0 {
|
|
return -val
|
|
}
|
|
return val
|
|
}
|
|
|
|
// wakeWaiters goes over all waiters and checks which of them can be notified.
|
|
func (s *sem) wakeWaiters() {
|
|
// Note that this will release all waiters waiting for 0 too.
|
|
for w := s.waiters.Front(); w != nil; {
|
|
if s.value < abs(w.value) {
|
|
// Still blocked, skip it.
|
|
w = w.Next()
|
|
continue
|
|
}
|
|
w.ch <- struct{}{}
|
|
old := w
|
|
w = w.Next()
|
|
s.waiters.Remove(old)
|
|
}
|
|
}
|
|
|
|
func newWaiter(val int16) *waiter {
|
|
return &waiter{
|
|
value: val,
|
|
ch: make(chan struct{}, 1),
|
|
}
|
|
}
|