blob: e46f9596a07c4428f4c071b78e9735df6735bd70 [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 (
"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, *Proxy, error) {
h0, h1, err := zx.NewChannel(0)
if err != nil {
return InterfaceRequest{}, nil, err
}
return InterfaceRequest{Channel: h0}, &Proxy{Channel: h1}, nil
}
// Proxy represents the client side of a FIDL interface.
type Proxy struct {
// Channel is the underlying channel endpoint for this interface.
zx.Channel
}
// IsValid returns true if the underlying channel is a valid handle.
func (p Proxy) IsValid() bool {
h := zx.Handle(p.Channel)
return h.IsValid()
}
// Send sends the request payload over the channel with the specified ordinal
// without a response.
func (p *Proxy) Send(ordinal uint32, req Payload) 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,
}
nb, nh, err := MarshalMessage(&header, req, respb[:], resph[:])
if err != nil {
return err
}
// Write the encoded bytes to the channel.
return p.Channel.Write(respb[:nb], resph[:nh], 0)
}
// Recv waits for an event and writes the response into the response payload.
func (p *Proxy) Recv(ordinal uint32, resp Payload) error {
respb := messageBytesPool.Get().([]byte)
resph := messageHandlesPool.Get().([]zx.Handle)
defer messageBytesPool.Put(respb)
defer messageHandlesPool.Put(resph)
// Wait on the channel to be readable or close.
h := zx.Handle(p.Channel)
sigs, err := zxwait.Wait(h,
zx.SignalChannelReadable|zx.SignalChannelPeerClosed,
zx.TimensecInfinite,
)
if err != nil {
return err
}
// If it is closed and not readable, let's just report that and stop here.
if (sigs&zx.SignalChannelPeerClosed) != 0 && (sigs&zx.SignalChannelReadable) == 0 {
return &zx.Error{Status: zx.ErrPeerClosed}
}
// Otherwise, now we can read!
nb, nh, err := p.Channel.Read(respb[:], resph[:], 0)
if err != nil {
return err
}
// Unmarshal the message.
var header MessageHeader
if err := UnmarshalMessage(respb[:nb], resph[:nh], &header, resp); err != nil {
return err
}
if header.Ordinal != ordinal {
return newExpectError(ErrUnexpectedOrdinal, ordinal, header.Ordinal)
}
return nil
}
// Call sends the request payload over the channel with the specified ordinal
// and synchronously waits for a response. It then writes the response into the
// response payload.
func (p *Proxy) Call(ordinal uint32, req Payload, resp Payload) 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{
Ordinal: ordinal,
}
nb, nh, err := MarshalMessage(&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
}
// Unmarshal the message.
if err := UnmarshalMessage(respb[:cnb], resph[:cnh], &header, resp); err != nil {
return err
}
if header.Ordinal != ordinal {
return newExpectError(ErrUnexpectedOrdinal, ordinal, header.Ordinal)
}
return nil
}
// 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 {
// Dispatch 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.
Dispatch(ordinal uint32, bytes []byte, handles []zx.Handle) (Payload, error)
}