blob: bda2cef443faf493003fb759dfa5bb622c7c05c9 [file] [log] [blame] [view]
# How to structure Fuchsia support for a language
This document describes the structure languages typically when supporting
Fuchsia.
## System calls
The lowest level of Fuchsia support in a language provides access to the
[Zircon system calls](/reference/syscalls).
Exposing these system calls lets programs written in the language interact with
the kernel and, transitively, with the rest of the system.
Programs cannot issue system calls directly. Instead, they make system calls by
calling functions in the [vDSO](/docs/concepts/kernel/vdso.md),
which is loaded into newly created processes by their creator.
The public entry points for the vDSO are defined in
[//zircon/vdso](/zircon/vdso/).
This file is processed by the [zither](/docs/concepts/kernel/vdso.md#zither_tool)
tool.
## Async
The vast majority of Fuchsia programs act as *servers*. After startup, they wait
in an event loop to receive messages, process those messages (potentially by
sending messages to other processes), and then go back to sleep in their event
loop.
The fundamental building block for event loops in Fuchsia is the
[port](/docs/reference/kernel_objects/port.md)
object. A thread can sleep in a port using
[`zx_port_wait`](/reference/syscalls/port_wait.md).
When the kernel wakes up the thread, the kernel provides a *packet*, which is a
data structure that describes why the kernel woke up the thread.
Typically, each thread has a single port object in which it sleeps, which a
significant amount of code written in your language will need to interact with.
Rather than expose the port directly, language maintainers usually provide
a library that abstracts over a port and provides asynchronous wait operations.
Most asynchronous wait operations bottom out in
[`zx_object_wait_async`](/reference/syscalls/object_wait_async.md). Typically, the `port` and `key`
arguments are provided by the library and the `handle` and `signals`
arguments are provided by the clients. When establishing a wait, the clients
also typically provide an upcall (e.g., a closure) for the library to invoke
when the wait completes, at which point the library uses the `key` to recover
the upcall (e.g., from a hash table).
No additional kernel object is needed to wake a thread up from another thread.
You can wake up a thread by simply queuing a user packet to the thread's port
using
[zx_port_queue](/reference/syscalls/port_queue.md).
### Examples
* [async](/zircon/system/ulib/async)
(C and C++)
* [fuchsia-async](/src/lib/fuchsia-async/) (Rust)
* [zxwait](https://fuchsia.googlesource.com/third_party/go/+/HEAD/src/syscall/zx/zxwait/) (Go)
## FIDL
The Zircon kernel itself largely provides memory management, scheduling, and
interprocess communication. Rather than being provided directly by the kernel,
the bulk of the system interface is actually provided through interprocess
communication, typically using [channels](/docs/reference/kernel_objects/channel.md).
The protocols used for interprocess communication are defined in
[Fuchsia Interface Definition Language (FIDL)](../fidl/README.md).
FIDL support for a language typically involves two pieces:
1. A language-specific backend for the FIDL compiler that generates code in the
target language.
2. A support library written in the target language that is used by the code
generated by the FIDL compiler.
These pieces are usually not built into the language implementation or runtime.
Instead, the libraries are part of the developer's program and versioned
independently from the language runtime. The stable interface between the
program and the language runtime should be the *system calls* rather than the
FIDL protocols so that developers can pick the versions of their FIDL
protocols and the version of their language runtimes independently.
In some cases, the language runtime might need to use FIDL internally. If that
happens, prefer to hide this implementation detail from the developer's program
if possible in your language. The developer might wish to use newer versions of
the same FIDL protocols without conflicting with the version used internally by
the language runtime.
### FIDL compiler backend
The [FIDL compiler](/tools/fidl/fidlc/)
has a single frontend that is used for all languages and multiple backends that
support a diverse assortment of languages. The frontend produces a
[JSON intermediate format][json-ir]
that is consumed by the language-specific backends.
You should create a new backend for the FIDL compiler for your language. The
backend can be written in whatever language you prefer. Typically, language
maintainers choose either Go or the target language. Official backends are named
`fidlgen_<lang>` and can be found in [//tools/fidl](/tools/fidl).
### Generated code
The generated FIDL code varies substantially from one language to another.
Typically the generated code will contain the following types of code:
* Data structure definitions that represent the data structures defined in the
[FIDL language][fidl-language].
* A codec that can serialize and deserialize these data structure into and from
the [FIDL wire format][fidl-wire-format].
* Stub objects that represent the server end of a FIDL protocol. Typically,
stub object have a *dispatch* method that deserializes a message read from a
Zircon channel and perform an indirect jump into an implementation of the
method specified by the message's *ordinal*.
* Proxy objects that represent the client end of a FIDL protocol. Typically,
method calls on proxy objects result in a message being serialized and
sent over a Zircon channel. Typically, proxy object have a *dispatch* for
event messages similar to the dispatch method found in stubs for request
messages.
Some languages offer multiple options for some of these types of generated code.
For example, a common pattern is to offer both *synchronous* and *asynchronous*
proxy objects. The synchronous proxies make use of
[`zx_channel_call`](/reference/syscalls/channel_call.md)
to efficiently write a message, block waiting for a response, and then read the
response, whereas asynchronous proxies use
[`zx_channel_write`](/reference/syscalls/channel_write.md),
[`zx_object_wait_async`](/reference/syscalls/object_wait_async.md),
and
[`zx_channel_read`](/reference/syscalls/channel_read.md)
to avoid blocking on the remote end of the channel.
Generally, we prefer to use *asynchronous* code whenever possible. Many FIDL
protocols are designed to be used in an asynchronous, feed-forward pattern.
### Support library
When designing the generated code for your language, pay particular attention to
binary size. Sophisticated program often interact with a large number of FIDL
protocols, each of which might define many data structures and protocols.
One important technique for reducing binary size is to factor as much code as
possible into a FIDL *support library*. For example, the C bindings, all the
serialization and deserialization logic is performed by a routine in a support
library. The generate code contains only a table that describes the wire format
in a compact form.
Typically, the support library is layered on top of the async library, which
itself has no knowledge of FIDL. For example, most support libraries contain a
*reader* object, which manages the asynchronous waiting and reading operations
on channels. The generated code can then be restricted to serialization,
deserialization, and dispatch.
* [C](/sdk/lib/fidl_base)
* [C++](/sdk/lib/fidl/cpp/)
* [Rust](/src/lib/fidl/rust/fidl)
* [Go](https://fuchsia.googlesource.com/third_party/go/+/HEAD/src/syscall/zx/fidl/)
## POSIX-style IO
POSIX-style IO operations (e.g., `open`, `close`, `read`, and `write`) are
layered on top of FIDL. If your language has C interop, you can use the
[FDIO library](/sdk/lib/fdio),
which translates familiar POSIX operations into the underlying `fuchsia.io` FIDL
protocol. If your language does not have C interop, you will need to interface
directly with `fuchsia.io` to provide POSIX-style IO.
You can recover the underlying Zircon handles for file descriptors using [`lib/fdio/unsafe.h`](/sdk/lib/fdio/include/lib/fdio/unsafe.h).
Typically, languages have a tiny library that layers on top of the async library
to perform asynchronous waits on file descriptors. This library typically
provides a less error-prone interface that abstracts these "unsafe" FDIO
functions.
<!-- xrefs -->
[json-ir]: /docs/reference/fidl/language/json-ir.md
[fidl-language]: /docs/reference/fidl/language/language.md
[fidl-wire-format]: /docs/reference/fidl/language/wire-format