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