// 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 kernel

import (
	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/bits"
)

const (
	// stdSignalCap is the maximum number of instances of a given standard
	// signal that may be pending. ("[If] multiple instances of a standard
	// signal are delivered while that signal is currently blocked, then only
	// one instance is queued.") - signal(7)
	stdSignalCap = 1

	// rtSignalCap is the maximum number of instances of a given realtime
	// signal that may be pending.
	//
	// TODO(igudger): In Linux, the minimum signal queue size is
	// RLIMIT_SIGPENDING, which is by default max_threads/2.
	rtSignalCap = 32
)

// pendingSignals holds a collection of pending signals. The zero value of
// pendingSignals is a valid empty collection. pendingSignals is thread-unsafe;
// users must provide synchronization.
//
// +stateify savable
type pendingSignals struct {
	// signals contains all pending signals.
	//
	// Note that signals is zero-indexed, but signal 1 is the first valid
	// signal, so signals[0] contains signals with signo 1 etc. This offset is
	// usually handled by using Signal.index().
	signals [linux.SignalMaximum]pendingSignalQueue `state:".([]savedPendingSignal)"`

	// Bit i of pendingSet is set iff there is at least one signal with signo
	// i+1 pending.
	pendingSet linux.SignalSet `state:"manual"`
}

// pendingSignalQueue holds a pendingSignalList for a single signal number.
//
// +stateify savable
type pendingSignalQueue struct {
	pendingSignalList
	length int
}

// +stateify savable
type pendingSignal struct {
	// pendingSignalEntry links into a pendingSignalList.
	pendingSignalEntry
	*linux.SignalInfo

	// If timer is not nil, it is the IntervalTimer which sent this signal.
	timer *IntervalTimer
}

// enqueue enqueues the given signal. enqueue returns true on success and false
// on failure (if the given signal's queue is full).
//
// Preconditions: info represents a valid signal.
func (p *pendingSignals) enqueue(info *linux.SignalInfo, timer *IntervalTimer) bool {
	sig := linux.Signal(info.Signo)
	q := &p.signals[sig.Index()]
	if sig.IsStandard() {
		if q.length >= stdSignalCap {
			return false
		}
	} else if q.length >= rtSignalCap {
		return false
	}
	q.pendingSignalList.PushBack(&pendingSignal{SignalInfo: info, timer: timer})
	q.length++
	p.pendingSet |= linux.SignalSetOf(sig)
	return true
}

// dequeue dequeues and returns any pending signal not masked by mask. If no
// unmasked signals are pending, dequeue returns nil.
func (p *pendingSignals) dequeue(mask linux.SignalSet) *linux.SignalInfo {
	// "Real-time signals are delivered in a guaranteed order. Multiple
	// real-time signals of the same type are delivered in the order they were
	// sent. If different real-time signals are sent to a process, they are
	// delivered starting with the lowest-numbered signal. (I.e., low-numbered
	// signals have highest priority.) By contrast, if multiple standard
	// signals are pending for a process, the order in which they are delivered
	// is unspecified. If both standard and real-time signals are pending for a
	// process, POSIX leaves it unspecified which is delivered first. Linux,
	// like many other implementations, gives priority to standard signals in
	// this case." - signal(7)
	lowestPendingUnblockedBit := bits.TrailingZeros64(uint64(p.pendingSet &^ mask))
	if lowestPendingUnblockedBit >= linux.SignalMaximum {
		return nil
	}
	return p.dequeueSpecific(linux.Signal(lowestPendingUnblockedBit + 1))
}

func (p *pendingSignals) dequeueSpecific(sig linux.Signal) *linux.SignalInfo {
	q := &p.signals[sig.Index()]
	ps := q.pendingSignalList.Front()
	if ps == nil {
		return nil
	}
	q.pendingSignalList.Remove(ps)
	q.length--
	if q.length == 0 {
		p.pendingSet &^= linux.SignalSetOf(sig)
	}
	if ps.timer != nil {
		ps.timer.updateDequeuedSignalLocked(ps.SignalInfo)
	}
	return ps.SignalInfo
}

// discardSpecific causes all pending signals with number sig to be discarded.
func (p *pendingSignals) discardSpecific(sig linux.Signal) {
	q := &p.signals[sig.Index()]
	for ps := q.pendingSignalList.Front(); ps != nil; ps = ps.Next() {
		if ps.timer != nil {
			ps.timer.signalRejectedLocked()
		}
	}
	q.pendingSignalList.Reset()
	q.length = 0
	p.pendingSet &^= linux.SignalSetOf(sig)
}
