summaryrefslogtreecommitdiffhomepage
path: root/pkg/flipcall/flipcall.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/flipcall/flipcall.go')
-rw-r--r--pkg/flipcall/flipcall.go247
1 files changed, 0 insertions, 247 deletions
diff --git a/pkg/flipcall/flipcall.go b/pkg/flipcall/flipcall.go
deleted file mode 100644
index 991018684..000000000
--- a/pkg/flipcall/flipcall.go
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2019 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 flipcall implements a protocol providing Fast Local Interprocess
-// Procedure Calls between mutually-distrusting processes.
-package flipcall
-
-import (
- "fmt"
- "math"
- "sync/atomic"
- "syscall"
-)
-
-// An Endpoint provides the ability to synchronously transfer data and control
-// to a connected peer Endpoint, which may be in another process.
-//
-// Since the Endpoint control transfer model is synchronous, at any given time
-// one Endpoint "has control" (designated the active Endpoint), and the other
-// is "waiting for control" (designated the inactive Endpoint). Users of the
-// flipcall package designate one Endpoint as the client, which is initially
-// active, and the other as the server, which is initially inactive. See
-// flipcall_example_test.go for usage.
-type Endpoint struct {
- // packet is a pointer to the beginning of the packet window. (Since this
- // is a raw OS memory mapping and not a Go object, it does not need to be
- // represented as an unsafe.Pointer.) packet is immutable.
- packet uintptr
-
- // dataCap is the size of the datagram part of the packet window in bytes.
- // dataCap is immutable.
- dataCap uint32
-
- // activeState is csClientActive if this is a client Endpoint and
- // csServerActive if this is a server Endpoint.
- activeState uint32
-
- // inactiveState is csServerActive if this is a client Endpoint and
- // csClientActive if this is a server Endpoint.
- inactiveState uint32
-
- // shutdown is non-zero if Endpoint.Shutdown() has been called, or if the
- // Endpoint has acknowledged shutdown initiated by the peer. shutdown is
- // accessed using atomic memory operations.
- shutdown uint32
-
- ctrl endpointControlImpl
-}
-
-// EndpointSide indicates which side of a connection an Endpoint belongs to.
-type EndpointSide int
-
-const (
- // ClientSide indicates that an Endpoint is a client (initially-active;
- // first method call should be Connect).
- ClientSide EndpointSide = iota
-
- // ServerSide indicates that an Endpoint is a server (initially-inactive;
- // first method call should be RecvFirst.)
- ServerSide
-)
-
-// Init must be called on zero-value Endpoints before first use. If it
-// succeeds, ep.Destroy() must be called once the Endpoint is no longer in use.
-//
-// pwd represents the packet window used to exchange data with the peer
-// Endpoint. FD may differ between Endpoints if they are in different
-// processes, but must represent the same file. The packet window must
-// initially be filled with zero bytes.
-func (ep *Endpoint) Init(side EndpointSide, pwd PacketWindowDescriptor, opts ...EndpointOption) error {
- switch side {
- case ClientSide:
- ep.activeState = csClientActive
- ep.inactiveState = csServerActive
- case ServerSide:
- ep.activeState = csServerActive
- ep.inactiveState = csClientActive
- default:
- return fmt.Errorf("invalid EndpointSide: %v", side)
- }
- if pwd.Length < pageSize {
- return fmt.Errorf("packet window size (%d) less than minimum (%d)", pwd.Length, pageSize)
- }
- if pwd.Length > math.MaxUint32 {
- return fmt.Errorf("packet window size (%d) exceeds maximum (%d)", pwd.Length, math.MaxUint32)
- }
- m, _, e := syscall.RawSyscall6(syscall.SYS_MMAP, 0, uintptr(pwd.Length), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED, uintptr(pwd.FD), uintptr(pwd.Offset))
- if e != 0 {
- return fmt.Errorf("failed to mmap packet window: %v", e)
- }
- ep.packet = m
- ep.dataCap = uint32(pwd.Length) - uint32(PacketHeaderBytes)
- if err := ep.ctrlInit(opts...); err != nil {
- ep.unmapPacket()
- return err
- }
- return nil
-}
-
-// NewEndpoint is a convenience function that returns an initialized Endpoint
-// allocated on the heap.
-func NewEndpoint(side EndpointSide, pwd PacketWindowDescriptor, opts ...EndpointOption) (*Endpoint, error) {
- var ep Endpoint
- if err := ep.Init(side, pwd, opts...); err != nil {
- return nil, err
- }
- return &ep, nil
-}
-
-// An EndpointOption configures an Endpoint.
-type EndpointOption interface {
- isEndpointOption()
-}
-
-// Destroy releases resources owned by ep. No other Endpoint methods may be
-// called after Destroy.
-func (ep *Endpoint) Destroy() {
- ep.unmapPacket()
-}
-
-func (ep *Endpoint) unmapPacket() {
- syscall.RawSyscall(syscall.SYS_MUNMAP, ep.packet, uintptr(ep.dataCap)+PacketHeaderBytes, 0)
- ep.packet = 0
-}
-
-// Shutdown causes concurrent and future calls to ep.Connect(), ep.SendRecv(),
-// ep.RecvFirst(), and ep.SendLast(), as well as the same calls in the peer
-// Endpoint, to unblock and return errors. It does not wait for concurrent
-// calls to return. Successive calls to Shutdown have no effect.
-//
-// Shutdown is the only Endpoint method that may be called concurrently with
-// other methods on the same Endpoint.
-func (ep *Endpoint) Shutdown() {
- if atomic.SwapUint32(&ep.shutdown, 1) != 0 {
- // ep.Shutdown() has previously been called.
- return
- }
- ep.ctrlShutdown()
-}
-
-// isShutdownLocally returns true if ep.Shutdown() has been called.
-func (ep *Endpoint) isShutdownLocally() bool {
- return atomic.LoadUint32(&ep.shutdown) != 0
-}
-
-type shutdownError struct{}
-
-// Error implements error.Error.
-func (shutdownError) Error() string {
- return "flipcall connection shutdown"
-}
-
-// DataCap returns the maximum datagram size supported by ep. Equivalently,
-// DataCap returns len(ep.Data()).
-func (ep *Endpoint) DataCap() uint32 {
- return ep.dataCap
-}
-
-// Connection state.
-const (
- // The client is, by definition, initially active, so this must be 0.
- csClientActive = 0
- csServerActive = 1
- csShutdown = 2
-)
-
-// Connect blocks until the peer Endpoint has called Endpoint.RecvFirst().
-//
-// Preconditions: ep is a client Endpoint. ep.Connect(), ep.RecvFirst(),
-// ep.SendRecv(), and ep.SendLast() have never been called.
-func (ep *Endpoint) Connect() error {
- return ep.ctrlConnect()
-}
-
-// RecvFirst blocks until the peer Endpoint calls Endpoint.SendRecv(), then
-// returns the datagram length specified by that call.
-//
-// Preconditions: ep is a server Endpoint. ep.SendRecv(), ep.RecvFirst(), and
-// ep.SendLast() have never been called.
-func (ep *Endpoint) RecvFirst() (uint32, error) {
- if err := ep.ctrlWaitFirst(); err != nil {
- return 0, err
- }
- recvDataLen := atomic.LoadUint32(ep.dataLen())
- if recvDataLen > ep.dataCap {
- return 0, fmt.Errorf("received packet with invalid datagram length %d (maximum %d)", recvDataLen, ep.dataCap)
- }
- return recvDataLen, nil
-}
-
-// SendRecv transfers control to the peer Endpoint, causing its call to
-// Endpoint.SendRecv() or Endpoint.RecvFirst() to return with the given
-// datagram length, then blocks until the peer Endpoint calls
-// Endpoint.SendRecv() or Endpoint.SendLast().
-//
-// Preconditions: dataLen <= ep.DataCap(). No previous call to ep.SendRecv() or
-// ep.RecvFirst() has returned an error. ep.SendLast() has never been called.
-// If ep is a client Endpoint, ep.Connect() has previously been called and
-// returned nil.
-func (ep *Endpoint) SendRecv(dataLen uint32) (uint32, error) {
- if dataLen > ep.dataCap {
- panic(fmt.Sprintf("attempting to send packet with datagram length %d (maximum %d)", dataLen, ep.dataCap))
- }
- // This store can safely be non-atomic: Under correct operation we should
- // be the only thread writing ep.dataLen(), and ep.ctrlRoundTrip() will
- // synchronize with the receiver. We will not read from ep.dataLen() until
- // after ep.ctrlRoundTrip(), so if the peer is mutating it concurrently then
- // they can only shoot themselves in the foot.
- *ep.dataLen() = dataLen
- if err := ep.ctrlRoundTrip(); err != nil {
- return 0, err
- }
- recvDataLen := atomic.LoadUint32(ep.dataLen())
- if recvDataLen > ep.dataCap {
- return 0, fmt.Errorf("received packet with invalid datagram length %d (maximum %d)", recvDataLen, ep.dataCap)
- }
- return recvDataLen, nil
-}
-
-// SendLast causes the peer Endpoint's call to Endpoint.SendRecv() or
-// Endpoint.RecvFirst() to return with the given datagram length.
-//
-// Preconditions: dataLen <= ep.DataCap(). No previous call to ep.SendRecv() or
-// ep.RecvFirst() has returned an error. ep.SendLast() has never been called.
-// If ep is a client Endpoint, ep.Connect() has previously been called and
-// returned nil.
-func (ep *Endpoint) SendLast(dataLen uint32) error {
- if dataLen > ep.dataCap {
- panic(fmt.Sprintf("attempting to send packet with datagram length %d (maximum %d)", dataLen, ep.dataCap))
- }
- *ep.dataLen() = dataLen
- if err := ep.ctrlWakeLast(); err != nil {
- return err
- }
- return nil
-}