Skip to content

Commit

Permalink
Implement enif_select and add select and mkfifo to file functions
Browse files Browse the repository at this point in the history
`enif_select` is an API that allows nifs to use `select(2)` or equivalent,
paving the way for nif-based drivers.

`enif_select` depends on platform implementations and this PR only implements
it on generic_unix platform, using `sys_poll_event` select-like function,
namely `kqueue(2)` or `poll(2)`. As a result, `enif_select` does not exactly
behaves like `select(2)` and this is documented.

For testing, add `mkfifo` and `select` to POSIX file functions.

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Jun 11, 2023
1 parent 9de5b4c commit 139ba21
Show file tree
Hide file tree
Showing 17 changed files with 820 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/libAtomVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ endfunction()
test_function_exists(open "fcntl.h" PUBLIC HAVE_OPEN)
test_function_exists(close "unistd.h" PUBLIC HAVE_CLOSE)
test_function_exists(unlink "unistd.h" PRIVATE HAVE_UNLINK)
test_function_exists(mkfifo "sys/stat.h" PRIVATE HAVE_MKFIFO)
test_symbol_exists(O_CLOEXEC "fcntl.h" PRIVATE HAVE_O_CLOEXEC)
test_symbol_exists(O_DIRECTORY "fcntl.h" PRIVATE HAVE_O_DIRECTORY)
test_symbol_exists(O_DSYNC "fcntl.h" PRIVATE HAVE_O_DSYNC)
Expand Down
8 changes: 8 additions & 0 deletions src/libAtomVM/defaultatoms.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ static const char *const info_atom = "\x4" "info";

static const char *const module_atom = "\x06" "module";

static const char *const select_atom = "\x6" "select";
static const char *const ready_input_atom = "\xB" "ready_input";
static const char *const ready_output_atom = "\xC" "ready_output";

void defaultatoms_init(GlobalContext *glb)
{
int ok = 1;
Expand Down Expand Up @@ -254,6 +258,10 @@ void defaultatoms_init(GlobalContext *glb)

ok &= globalcontext_insert_atom(glb, module_atom) == MODULE_ATOM_INDEX;

ok &= globalcontext_insert_atom(glb, select_atom) == SELECT_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, ready_input_atom) == READY_INPUT_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, ready_output_atom) == READY_OUTPUT_ATOM_INDEX;

if (!ok) {
AVM_ABORT();
}
Expand Down
114 changes: 114 additions & 0 deletions src/libAtomVM/defaultatoms.gperf
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* This file is part of AtomVM.
*
* Copyright 2023 Paul Guyot <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
*/

%readonly-tables
%define lookup-function-name defaultatom_in_word_set
%pic

%{
#include <defaultatom.h>
%}
%%
"\005false", FALSE_ATOM_INDEX
"\004true", TRUE_ATOM_INDEX
"\002ok", OK_ATOM_INDEX
"\005error", ERROR_ATOM_INDEX
"\011undefined", UNDEFINED_ATOM_INDEX
"\006badarg", BADARG_ATOM_INDEX
"\010badarith", BADARITH_ATOM_INDEX
"\010badarity", BADARITY_ATOM_INDEX
"\006badfun", BADFUN_ATOM_INDEX
"\014system_limit", SYSTEM_LIMIT_ATOM_INDEX
"\017function_clause", FUNCTION_CLAUSE_ATOM_INDEX
"\012try_clause", TRY_CLAUSE_ATOM_INDEX
"\015out_of_memory", OUT_OF_MEMORY_ATOM_INDEX
"\010overflow", OVERFLOW_ATOM_INDEX
"\005flush", FLUSH_ATOM_INDEX
"\011heap_size", HEAP_SIZE_ATOM_INDEX
"\006latin1", LATIN1_ATOM_INDEX
"\015max_heap_size", MAX_HEAP_SIZE_ATOM_INDEX
"\006memory", MEMORY_ATOM_INDEX
"\021message_queue_len", MESSAGE_QUEUE_LEN_ATOM_INDEX
"\004puts", PUTS_ATOM_INDEX
"\012stack_size", STACK_SIZE_ATOM_INDEX
"\015min_heap_size", MIN_HEAP_SIZE_ATOM_INDEX
"\015process_count", PROCESS_COUNT_ATOM_INDEX
"\012port_count", PORT_COUNT_ATOM_INDEX
"\012atom_count", ATOM_COUNT_ATOM_INDEX
"\023system_architecture", SYSTEM_ARCHITECTURE_ATOM_INDEX
"\010wordsize", WORDSIZE_ATOM_INDEX
"\010decimals", DECIMALS_ATOM_INDEX
"\012scientific", SCIENTIFIC_ATOM_INDEX
"\007compact", COMPACT_ATOM_INDEX
"\010badmatch", BADMATCH_ATOM_INDEX
"\013case_clause", CASE_CLAUSE_ATOM_INDEX
"\011if_clause", IF_CLAUSE_ATOM_INDEX
"\005throw", THROW_ATOM_INDEX
"\013low_entropy", LOW_ENTROPY_ATOM_INDEX
"\013unsupported", UNSUPPORTED_ATOM_INDEX
"\004used", USED_ATOM_INDEX
"\003all", ALL_ATOM_INDEX
"\005start", START_ATOM_INDEX
"\005undef", UNDEF_ATOM_INDEX
"\010vm_abort", VM_ABORT_ATOM_INDEX
"\004link", LINK_ATOM_INDEX
"\007monitor", MONITOR_ATOM_INDEX
"\006normal", NORMAL_ATOM_INDEX
"\004DOWN", DOWN_ATOM_INDEX
"\007process", PROCESS_ATOM_INDEX
"\007nocatch", NOCATCH_ATOM_INDEX
"\020refc_binary_info", REFC_BINARY_INFO_ATOM_INDEX
"\006noproc", NOPROC_ATOM_INDEX
"\011trap_exit", TRAP_EXIT_ATOM_INDEX
"\004EXIT", EXIT_ATOM_INDEX
"\006badmap", BADMAP_ATOM_INDEX
"\006badkey", BADKEY_ATOM_INDEX
"\004none", NONE_ATOM_INDEX
"\012io_request", IO_REQUEST_ATOM_INDEX
"\010io_reply", IO_REPLY_ATOM_INDEX
"\011put_chars", PUT_CHARS_ATOM_INDEX
"\004exit", LOWERCASE_EXIT_ATOM_INDEX
"\016atomvm_version", ATOMVM_VERSION_ATOM_INDEX
"\006second", SECOND_ATOM_INDEX
"\013millisecond", MILLISECOND_ATOM_INDEX
"\013microsecond", MICROSECOND_ATOM_INDEX
"\010infinity", INFINITY_ATOM_INDEX
"\015timeout_value", TIMEOUT_VALUE_ATOM_INDEX
"\007machine", MACHINE_ATOM_INDEX
"\015avm_floatsize", AVM_FLOATSIZE_ATOM_INDEX
"\006append", APPEND_ATOM_INDEX
"\016private_append", PRIVATE_APPEND_ATOM_INDEX
"\006binary", BINARY_ATOM_INDEX
"\007integer", INTEGER_ATOM_INDEX
"\006little", LITTLE_ATOM_INDEX
"\006native", NATIVE_ATOM_INDEX
"\006string", STRING_ATOM_INDEX
"\004utf8", UTF8_ATOM_INDEX
"\005utf16", UTF16_ATOM_INDEX
"\005utf32", UTF32_ATOM_INDEX
"\011badrecord", BADRECORD_ATOM_INDEX
"\004copy", COPY_ATOM_INDEX
"\005reuse", REUSE_ATOM_INDEX
"\017ensure_at_least", ENSURE_AT_LEAST_ATOM_INDEX
"\016ensure_exactly", ENSURE_EXACTLY_ATOM_INDEX
"\004skip", SKIP_ATOM_INDEX
"\010get_tail", GET_TAIL_ATOM_INDEX
"\003=:=", EQUAL_COLON_EQUAL_ATOM_INDEX
"\006signed", SIGNED_ATOM_INDEX
10 changes: 9 additions & 1 deletion src/libAtomVM/defaultatoms.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,11 @@ extern "C" {

#define MODULE_ATOM_INDEX 92

#define PLATFORM_ATOMS_BASE_INDEX 93
#define SELECT_ATOM_INDEX 93
#define READY_INPUT_ATOM_INDEX 94
#define READY_OUTPUT_ATOM_INDEX 95

#define PLATFORM_ATOMS_BASE_INDEX 96

#define FALSE_ATOM TERM_FROM_ATOM_INDEX(FALSE_ATOM_INDEX)
#define TRUE_ATOM TERM_FROM_ATOM_INDEX(TRUE_ATOM_INDEX)
Expand Down Expand Up @@ -263,6 +267,10 @@ extern "C" {

#define MODULE_ATOM TERM_FROM_ATOM_INDEX(MODULE_ATOM_INDEX)

#define SELECT_ATOM TERM_FROM_ATOM_INDEX(SELECT_ATOM_INDEX)
#define READY_INPUT_ATOM TERM_FROM_ATOM_INDEX(READY_INPUT_ATOM_INDEX)
#define READY_OUTPUT_ATOM TERM_FROM_ATOM_INDEX(READY_OUTPUT_ATOM_INDEX)

void defaultatoms_init(GlobalContext *glb);

void platform_defaultatoms_init(GlobalContext *glb);
Expand Down
61 changes: 60 additions & 1 deletion src/libAtomVM/erl_nif.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,21 @@ typedef int ErlNifEvent;
*/
typedef void ErlNifResourceDtor(ErlNifEnv *caller_env, void *obj);

/**
* @brief Select stop callback
*/
typedef void ErlNifResourceStop(ErlNifEnv *caller_env, void *obj, ErlNifEvent event, int is_direct_call);

/**
* @brief Resource callbacks.
* @details Members should be set to 0, 1 depending on provided callbacks.
* @details Members should be set to 0, 1 or 2 depending on provided callbacks.
* Callbacks can also be NULL if not used.
*/
typedef struct
{
int members;
ErlNifResourceDtor *dtor;
ErlNifResourceStop *stop;
} ErlNifResourceTypeInit;

/**
Expand All @@ -83,6 +89,35 @@ typedef enum
// ERL_NIF_RT_TAKEOVER is not supported yet
} ErlNifResourceFlags;

/**
* @brief enif_select mode flags
* @details ERL_NIF_SELECT_CANCEL which was introduced with OTP-22, is unimplemented.
*/
enum ErlNifSelectFlags
{
ERL_NIF_SELECT_READ = 1,
ERL_NIF_SELECT_WRITE = 2,
ERL_NIF_SELECT_STOP = 4,
// ERL_NIF_SELECT_CANCEL = 8,
};

/**
* @brief enif_select result flags
* @details ERL_NIF_SELECT_CANCEL which was introduced with OTP-22, is unimplemented.
*/
enum
{
ERL_NIF_SELECT_STOP_CALLED = 1,
ERL_NIF_SELECT_STOP_SCHEDULED = 2,
// ERL_NIF_SELECT_READ_CANCELLED = 4,
// ERL_NIF_SELECT_WRITE_CANCELLED = 8,

ERL_NIF_SELECT_INVALID_EVENT = -1,
ERL_NIF_SELECT_FAILED = -2,

ERL_NIF_SELECT_BADARG = -3,
};

/**
* @brief Create or take over (code upgrade) a resource type.
* @param env the current environment
Expand Down Expand Up @@ -143,6 +178,30 @@ int enif_release_resource(void *resource);
*/
ERL_NIF_TERM enif_make_resource(ErlNifEnv *env, void *obj);

/**
* @brief Run a POSIX-like select on a given object (event) and send a message
* when the object is readable or writable.
*
* @details Actual implementation is platform dependent and platforms may not
* implement this feature.
*
* On `generic_unix`, this is currently implemented using what
* `sys_poll_events` uses, namely `kqueue(2)` (if available) or `poll(2)`.
* Please note that `kqueue(2)` and `poll(2)` behave differently for some
* objects, for example for vnodes and EOF.
*
* @param env current environment
* @param event event object (typically a file descriptor)
* @param mode select mode (`ERL_NIF_SELECT_READ` and/or `ERL_NIF_SELECT_WRITE`)
* optionally with `ERL_NIF_SELECT_CANCEL` to cancel, or `ERL_NIF_SELECT_STOP`
* to stop.
* @param obj resource object working as a container of the event object.
* @param pid process id to send a message to or NULL to use the current process (from `env`)
* @param ref reference object used in sent messages or `undefined` atom.
* @return a negative value on failure, 0 or flags on success.
*/
int enif_select(ErlNifEnv *env, ErlNifEvent event, enum ErlNifSelectFlags mode, void *obj, const ErlNifPid *pid, ERL_NIF_TERM ref);

#ifdef __cplusplus
}
#endif
Expand Down
8 changes: 8 additions & 0 deletions src/libAtomVM/globalcontext.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ GlobalContext *globalcontext_new()
synclist_init(&glb->registered_processes);
synclist_init(&glb->listeners);
synclist_init(&glb->resource_types);
synclist_init(&glb->select_events);

glb->last_process_id = 0;

Expand Down Expand Up @@ -212,6 +213,13 @@ COLD_FUNC void globalcontext_destroy(GlobalContext *glb)
}
synclist_destroy(&glb->resource_types);

struct ListHead *select_events = synclist_nolock(&glb->select_events);
MUTABLE_LIST_FOR_EACH (item, tmp, select_events) {
struct SelectEvent *select_event = GET_LIST_ENTRY(item, struct SelectEvent, head);
free((void *) select_event);
}
synclist_destroy(&glb->select_events);

free(glb);
}

Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/globalcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct GlobalContext
struct SyncList registered_processes;
struct SyncList listeners;
struct SyncList resource_types;
struct SyncList select_events;

int32_t last_process_id;

Expand Down
Loading

0 comments on commit 139ba21

Please sign in to comment.