Skip to content

Commit

Permalink
add descriptor table for mapping fds to handles (#464)
Browse files Browse the repository at this point in the history
* add descriptor table for mapping fds to handles

This introduces `descriptor_table.h` and `descriptor_table.c`, providing a
global hashtable for tracking `wasi-libc`-managed file descriptors.

WASI Preview 2 has no notion of file descriptors and instead uses unforgeable
resource handles.  Moreover, there's not necessarily a one-to-one correspondence
between POSIX file descriptors and resource handles (e.g. a TCP connection may
require separate handles for reading, writing, and polling the same connection).
We use this table to map each POSIX descriptor to a set of one or more handles
and any extra state which libc needs to track.

Note that we've added `descriptor_table.h` to the
libc-bottom-half/headers/public/wasi directory, making it part of the public
API.  The intention is to give applications access to the mapping, enabling them
to convert descriptors to handles and vice-versa should they need to
interoperate with both libc and WASI directly.

Co-authored-by: Dave Bakker <[email protected]>
Signed-off-by: Joel Dice <[email protected]>

* add dummy fields to otherwise empty structs

The C standard doesn't allow empty structs.  Clang doesn't currently complain,
but we might as well stick to the spec in case it becomes more strict in the
future.

Signed-off-by: Joel Dice <[email protected]>

* move descriptor_table.h to headers/private

We're not yet ready to commit to making this API public, so we'll make it
private for now.

I've also expanded a comment in descriptor_table.c to explain the current ABI
for resource handles.

Signed-off-by: Joel Dice <[email protected]>

* re-run clang-format to fix indentation

Signed-off-by: Joel Dice <[email protected]>

---------

Signed-off-by: Joel Dice <[email protected]>
Co-authored-by: Dave Bakker <[email protected]>
  • Loading branch information
dicej and badeend committed Feb 27, 2024
1 parent 0fe51d2 commit 09683b3
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 3 deletions.
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ ifeq ($(WASI_SNAPSHOT), preview1)
# Omit source files not relevant to WASI Preview 1. As we introduce files
# supporting `wasi-sockets` for `wasm32-wasi-preview2`, we'll add those files to
# this list.
LIBC_BOTTOM_HALF_OMIT_SOURCES := $(LIBC_BOTTOM_HALF_SOURCES)/preview2.c
LIBC_BOTTOM_HALF_OMIT_SOURCES := \
$(LIBC_BOTTOM_HALF_SOURCES)/preview2.c \
$(LIBC_BOTTOM_HALF_SOURCES)/descriptor_table.c
LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_OMIT_SOURCES),$(LIBC_BOTTOM_HALF_ALL_SOURCES))
# Omit preview2.h from include-all.c test.
INCLUDE_ALL_CLAUSES := -not -name preview2.h
# Omit preview2-specific headers from include-all.c test.
INCLUDE_ALL_CLAUSES := -not -name preview2.h -not -name descriptor_table.h
endif

# FIXME(https://reviews.llvm.org/D85567) - due to a bug in LLD the weak
Expand Down
3 changes: 3 additions & 0 deletions expected/wasm32-wasi-preview2/defined-symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ ctanhl
ctanl
ctime
ctime_r
descriptor_table_get_ref
descriptor_table_insert
descriptor_table_remove
difftime
dirfd
dirname
Expand Down
2 changes: 2 additions & 0 deletions expected/wasm32-wasi-preview2/undefined-symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ __subtf3
__trunctfdf2
__trunctfsf2
__unordtf2
__wasi_preview1_adapter_close_badfd
__wasi_preview1_adapter_open_badfd
__wasm_call_ctors
__wasm_import_environment_get_arguments
__wasm_import_environment_get_environment
Expand Down
127 changes: 127 additions & 0 deletions libc-bottom-half/headers/private/wasi/descriptor_table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#ifndef DESCRIPTOR_TABLE_H
#define DESCRIPTOR_TABLE_H

#include <wasi/preview2.h>

typedef struct {
int dummy;
} tcp_socket_state_unbound_t;
typedef struct {
int dummy;
} tcp_socket_state_bound_t;
typedef struct {
int dummy;
} tcp_socket_state_connecting_t;
typedef struct {
int dummy;
} tcp_socket_state_listening_t;

typedef struct {
streams_own_input_stream_t input;
poll_own_pollable_t input_pollable;
streams_own_output_stream_t output;
poll_own_pollable_t output_pollable;
} tcp_socket_state_connected_t;

typedef struct {
network_error_code_t error_code;
} tcp_socket_state_connect_failed_t;

// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
typedef struct {
enum {
TCP_SOCKET_STATE_UNBOUND,
TCP_SOCKET_STATE_BOUND,
TCP_SOCKET_STATE_CONNECTING,
TCP_SOCKET_STATE_CONNECTED,
TCP_SOCKET_STATE_CONNECT_FAILED,
TCP_SOCKET_STATE_LISTENING,
} tag;
union {
tcp_socket_state_unbound_t unbound;
tcp_socket_state_bound_t bound;
tcp_socket_state_connecting_t connecting;
tcp_socket_state_connected_t connected;
tcp_socket_state_connect_failed_t connect_failed;
tcp_socket_state_listening_t listening;
};
} tcp_socket_state_t;

typedef struct {
tcp_own_tcp_socket_t socket;
poll_own_pollable_t socket_pollable;
bool blocking;
bool fake_nodelay;
bool fake_reuseaddr;
network_ip_address_family_t family;
tcp_socket_state_t state;
} tcp_socket_t;

typedef struct {
udp_own_incoming_datagram_stream_t incoming;
poll_own_pollable_t incoming_pollable;
udp_own_outgoing_datagram_stream_t outgoing;
poll_own_pollable_t outgoing_pollable;
} udp_socket_streams_t;

typedef struct {
int dummy;
} udp_socket_state_unbound_t;
typedef struct {
int dummy;
} udp_socket_state_bound_nostreams_t;

typedef struct {
udp_socket_streams_t streams; // Streams have no remote_address
} udp_socket_state_bound_streaming_t;

typedef struct {
udp_socket_streams_t streams; // Streams have a remote_address
} udp_socket_state_connected_t;

// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
// The "bound" state is split up into two distinct tags:
// - "bound_nostreams": Bound, but no datagram streams set up (yet). That will be done the first time send or recv is called.
// - "bound_streaming": Bound with active streams.
typedef struct {
enum {
UDP_SOCKET_STATE_UNBOUND,
UDP_SOCKET_STATE_BOUND_NOSTREAMS,
UDP_SOCKET_STATE_BOUND_STREAMING,
UDP_SOCKET_STATE_CONNECTED,
} tag;
union {
udp_socket_state_unbound_t unbound;
udp_socket_state_bound_nostreams_t bound_nostreams;
udp_socket_state_bound_streaming_t bound_streaming;
udp_socket_state_connected_t connected;
};
} udp_socket_state_t;

typedef struct {
udp_own_udp_socket_t socket;
poll_own_pollable_t socket_pollable;
bool blocking;
network_ip_address_family_t family;
udp_socket_state_t state;
} udp_socket_t;

// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
typedef struct {
enum {
DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET,
DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET,
} tag;
union {
tcp_socket_t tcp_socket;
udp_socket_t udp_socket;
};
} descriptor_table_entry_t;

bool descriptor_table_insert(descriptor_table_entry_t entry, int *fd);

bool descriptor_table_get_ref(int fd, descriptor_table_entry_t **entry);

bool descriptor_table_remove(int fd, descriptor_table_entry_t *entry);

#endif
Loading

0 comments on commit 09683b3

Please sign in to comment.