blob: 364767aca606a930e55beedf9bb08c5844e6cf19 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build fuchsia
package fidl
import (
"strconv"
"syscall/zx"
"syscall/zx/zxwait"
)
// ServiceRequest is an abstraction over a FIDL interface request which is
// intended to be used as part of service discovery.
type ServiceRequest interface {
// Name returns the name of the service being requested.
Name() string
// ToChannel returns the underlying channel of the ServiceRequest.
ToChannel() zx.Channel
}
// InterfaceRequest is a wrapper type around a channel and represents the server side
// of a FIDL interface, to be sent to a server as a request.
type InterfaceRequest struct {
zx.Channel
}
// NewInterfaceRequest generates two sides of a channel with one layer of
// type casts out of the way to minimize the amount of generated code. Semantically,
// the two sides of the channel represent the interface request and the client
// side of the interface (the proxy). It returns an error on failure.
func NewInterfaceRequest() (InterfaceRequest, *ChannelProxy, error) {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return InterfaceRequest{}, nil, err
}
return InterfaceRequest{Channel: h0}, &ChannelProxy{Channel: h1}, nil
}
// Proxy represents the client side of a FIDL interface.
type Proxy interface {
IsValid() bool
Send(ordinal uint64, req Message) error
// TODO(FIDL-524): Remove `gen_ordinal` post-ordinal migration.
Recv(ordinal uint64, resp Message, gen_ordinal ...uint64) error
Call(ordinal uint64, req Message, resp Message, gen_ordinal ...uint64) error
}
// Stub represents a generated type which wraps the server-side implementation of a
// FIDL interface.
//
// It contains logic which is able to dispatch into the correct implementation given
// the incoming message ordinal and its data.
type Stub interface {
// DispatchImpl dispatches into the appropriate method implementation for a FIDL
// interface by using the ordinal.
//
// It also takes the data as bytes and transforms it into arguments usable by
// the method implementation. It then optionally returns a response if the
// method has a response, in which case, the boolean return value is true.
DispatchImpl(ordinal uint64, bytes []byte, handles []zx.Handle) (Message, bool, error)
}
// ChannelProxy a Proxy that is backed by a channel.
type ChannelProxy struct {
// Channel is the underlying channel endpoint for this interface.
zx.Channel
}
// Assert that ChannelProxy implements the Proxy interface.
var _ Proxy = &ChannelProxy{}
// IsValid returns true if the underlying channel is a valid handle.
func (p *ChannelProxy) IsValid() bool {
return p.Channel.Handle().IsValid()
}
func withRetry(fn func() error, handle zx.Handle, ready, closed zx.Signals) error {
signals := ready | closed
for {
err := fn()
if err, ok := err.(*zx.Error); ok && err.Status == zx.ErrShouldWait {
obs, err := zxwait.Wait(
handle,
signals,
zx.TimensecInfinite,
)
if err != nil {
return err
}
if obs&ready != 0 {
continue
}
if obs&closed != 0 {
return &zx.Error{Status: zx.ErrPeerClosed}
}
panic("unexpected signal mask " + strconv.FormatUint(uint64(obs), 2) + " (expected " + strconv.FormatUint(uint64(signals), 2) + ")")
}
return err
}
}
func unmarshalForOrdinal(ordinal uint64, respb []byte, resph []zx.Handle, resp Message, gen_ordinal ...uint64) error {
var header MessageHeader
if err := UnmarshalHeaderThenMessage(respb, resph, &header, resp); err != nil {
return err
}
if !header.IsSupportedVersion() {
return ErrUnknownMagic
}
// TODO(FIDL-524): Remove temporary handling of two ordinals.
expectedOrdinal := header.Ordinal == ordinal
expectedOrdinals := []uint64{ordinal}
for _, ordinal := range gen_ordinal {
expectedOrdinal = expectedOrdinal || header.Ordinal == ordinal
expectedOrdinals = append(expectedOrdinals, ordinal)
}
if !expectedOrdinal {
return newExpectError(ErrUnexpectedOrdinal, expectedOrdinals, header.Ordinal)
}
return nil
}
// Send sends the request over the channel with the specified ordinal
// without a response.
func (p *ChannelProxy) Send(ordinal uint64, req Message) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
defer messageBytesPool.Put(respb)
defer messageHandlesPool.Put(resph)
// Marshal the message into the buffer.
header := MessageHeader{
Txid: 0, // Txid == 0 for messages without a response.
Ordinal: ordinal,
Magic: FidlWireFormatMagicNumberInitial,
}
if newCtx().EncodeUnionsAsXUnionBytes {
header.SetUnionFromXunionBytes()
}
nb, nh, err := MarshalHeaderThenMessage(&header, req, respb[:], resph[:])
if err != nil {
return err
}
// Write the encoded bytes to the channel.
return withRetry(func() error {
return p.Channel.Write(respb[:nb], resph[:nh], 0)
}, *p.Channel.Handle(), zx.SignalChannelWritable, zx.SignalChannelPeerClosed)
}
// Recv waits for an event and writes the response into the response.
func (p *ChannelProxy) Recv(ordinal uint64, resp Message, gen_ordinal ...uint64) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
defer messageBytesPool.Put(respb)
defer messageHandlesPool.Put(resph)
// close channel on error
hasFailed := true
defer func() {
if hasFailed {
p.Channel.Close()
}
}()
var nb, nh uint32
if err := withRetry(func() error {
var err error
nb, nh, err = p.Channel.Read(respb[:], resph[:], 0)
return err
}, *p.Channel.Handle(), zx.SignalChannelReadable, zx.SignalChannelPeerClosed); err != nil {
return err
}
err := unmarshalForOrdinal(ordinal, respb[:nb], resph[:nh], resp, gen_ordinal...)
if err != nil {
return err
}
hasFailed = false
return nil
}
// Call sends the request over the channel with the specified ordinal
// and synchronously waits for a response. It then writes the response into the
// response.
func (p *ChannelProxy) Call(ordinal uint64, req Message, resp Message, gen_ordinal ...uint64) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
defer messageBytesPool.Put(respb)
defer messageHandlesPool.Put(resph)
// close channel on error
hasFailed := true
defer func() {
if hasFailed {
p.Channel.Close()
}
}()
// Marshal the message into the buffer
header := MessageHeader{
Ordinal: ordinal,
Magic: FidlWireFormatMagicNumberInitial,
}
if newCtx().EncodeUnionsAsXUnionBytes {
header.SetUnionFromXunionBytes()
}
nb, nh, err := MarshalHeaderThenMessage(&header, req, respb[:], resph[:])
if err != nil {
return err
}
// Make the IPC call.
cnb, cnh, err := p.Channel.Call(0, zx.TimensecInfinite, respb[:nb], resph[:nh], respb[:], resph[:])
if err != nil {
return err
}
err = unmarshalForOrdinal(ordinal, respb[:cnb], resph[:cnh], resp, gen_ordinal...)
if err != nil {
return err
}
hasFailed = false
return nil
}