Skip to content

Commit

Permalink
Merge pull request #611 from pguyot/w21/unix-file-nif
Browse files Browse the repository at this point in the history
Implement basic POSIX file functions as nifs

NIFs are compiled if platform features common POSIX functions `open`, `close`,
`read`, `write` and `unlink`.

Implementation is non-blocking and a more complete implementation of a file
module may require `enif_select`, at least to work with FIFO pipes and devices.

On ESP32, implementation depends on vfs_fat driver which is always blocking
(and does not support select). Qemu-based test writes to a (virtual) SD card.
However, support for SD card still requires C code to mount it which is not
the object of this PR.

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
  • Loading branch information
bettio committed Jul 2, 2023
2 parents 83a8779 + 9dde47e commit 89700dc
Show file tree
Hide file tree
Showing 22 changed files with 872 additions and 18 deletions.
40 changes: 40 additions & 0 deletions CMakeModules/DefineIfExists.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# 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
#

include(CheckSymbolExists)
include(CheckCSourceCompiles)

function(define_if_symbol_exists target symbol header scope macro)
check_symbol_exists(${symbol} ${header} ${macro})
if (${macro})
target_compile_definitions(${target} ${scope} ${macro})
endif(${macro})
endfunction()
function(define_if_function_exists target symbol header scope macro)
check_c_source_compiles("
#include <${header}>
int main(int argc)
{
return ((int*)(&${symbol}))[argc];
}" ${macro})
if (${macro})
target_compile_definitions(${target} ${scope} ${macro})
endif(${macro})
endfunction()
18 changes: 17 additions & 1 deletion src/libAtomVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ set(HEADER_FILES
nifs.h
platform_nifs.h
port.h
posix_nifs.h
refc_binary.h
resources.h
scheduler.h
Expand Down Expand Up @@ -84,6 +85,7 @@ set(SOURCE_FILES
module.c
nifs.c
port.c
posix_nifs.c
refc_binary.c
resources.c
scheduler.c
Expand All @@ -109,7 +111,7 @@ endif()

target_link_libraries(libAtomVM PUBLIC m)
include(CheckCSourceCompiles)
set(CMAKE_REQUIRED_FLAGS -Werror=unknown-pragmas)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unknown-pragmas")
check_c_source_compiles("
#include <fenv.h>
int main() {
Expand Down Expand Up @@ -150,6 +152,20 @@ else()
endif()
endif()

include(DefineIfExists)
# HAVE_OPEN & HAVE_CLOSE are used in globalcontext.h
define_if_function_exists(libAtomVM open "fcntl.h" PUBLIC HAVE_OPEN)
define_if_function_exists(libAtomVM close "unistd.h" PUBLIC HAVE_CLOSE)
define_if_function_exists(libAtomVM unlink "unistd.h" PRIVATE HAVE_UNLINK)
define_if_symbol_exists(libAtomVM O_CLOEXEC "fcntl.h" PRIVATE HAVE_O_CLOEXEC)
define_if_symbol_exists(libAtomVM O_DIRECTORY "fcntl.h" PRIVATE HAVE_O_DIRECTORY)
define_if_symbol_exists(libAtomVM O_DSYNC "fcntl.h" PRIVATE HAVE_O_DSYNC)
define_if_symbol_exists(libAtomVM O_EXEC "fcntl.h" PRIVATE HAVE_O_EXEC)
define_if_symbol_exists(libAtomVM O_NOFOLLOW "fcntl.h" PRIVATE HAVE_O_NOFOLLOW)
define_if_symbol_exists(libAtomVM O_RSYNC "fcntl.h" PRIVATE HAVE_O_RSYNC)
define_if_symbol_exists(libAtomVM O_SEARCH "fcntl.h" PRIVATE HAVE_O_SEARCH)
define_if_symbol_exists(libAtomVM O_TTY_INIT "fcntl.h" PRIVATE HAVE_O_TTY_INIT)

if (AVM_USE_32BIT_FLOAT)
target_compile_definitions(libAtomVM PUBLIC AVM_USE_SINGLE_PRECISION)
endif()
Expand Down
24 changes: 24 additions & 0 deletions src/libAtomVM/globalcontext.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
#include "avmpack.h"
#include "context.h"
#include "defaultatoms.h"
#include "erl_nif_priv.h"
#include "list.h"
#include "posix_nifs.h"
#include "refc_binary.h"
#include "resources.h"
#include "synclist.h"
Expand Down Expand Up @@ -126,11 +128,30 @@ GlobalContext *globalcontext_new()
smp_spinlock_init(&glb->ref_ticks_spinlock);
#endif

#if HAVE_OPEN && HAVE_CLOSE
ErlNifEnv env;
erl_nif_env_partial_init_from_globalcontext(&env, glb);
glb->posix_fd_resource_type = enif_init_resource_type(&env, "posix_fd", &posix_fd_resource_type_init, ERL_NIF_RT_CREATE, NULL);
if (IS_NULL_PTR(glb->posix_fd_resource_type)) {
#ifndef AVM_NO_SMP
smp_rwlock_destroy(glb->modules_lock);
#endif
free(glb->modules_table);
free(glb->atoms_ids_table);
free(glb->atoms_table);
free(glb);
return NULL;
}
#endif

sys_init_platform(glb);

#ifndef AVM_NO_SMP
glb->schedulers_mutex = smp_mutex_create();
if (IS_NULL_PTR(glb->schedulers_mutex)) {
#if HAVE_OPEN && HAVE_CLOSE
resource_type_destroy(glb->posix_fd_resource_type);
#endif
smp_rwlock_destroy(glb->modules_lock);
free(glb->modules_table);
free(glb->atoms_ids_table);
Expand All @@ -141,6 +162,9 @@ GlobalContext *globalcontext_new()
glb->schedulers_cv = smp_condvar_create();
if (IS_NULL_PTR(glb->schedulers_cv)) {
smp_mutex_destroy(glb->schedulers_mutex);
#if HAVE_OPEN && HAVE_CLOSE
resource_type_destroy(glb->posix_fd_resource_type);
#endif
smp_rwlock_destroy(glb->modules_lock);
free(glb->modules_table);
free(glb->atoms_ids_table);
Expand Down
5 changes: 5 additions & 0 deletions src/libAtomVM/globalcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extern "C" {
#include <stdint.h>

#include "atom.h"
#include "erl_nif.h"
#include "list.h"
#include "smp.h"
#include "synclist.h"
Expand Down Expand Up @@ -121,6 +122,10 @@ struct GlobalContext
SpinLock env_spinlock;
#endif

#if HAVE_OPEN && HAVE_CLOSE
ErlNifResourceType *posix_fd_resource_type;
#endif

void *platform_data;
};

Expand Down
32 changes: 19 additions & 13 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@

#include "nifs.h"

#include <fenv.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "atomshashtable.h"
#include "avmpack.h"
#include "bif.h"
Expand All @@ -36,29 +42,17 @@
#include "module.h"
#include "platform_nifs.h"
#include "port.h"
#include "posix_nifs.h"
#include "scheduler.h"
#include "smp.h"
#include "sys.h"
#include "term.h"
#include "utils.h"
#include "version.h"

#include <stdio.h>
#include <string.h>
#include <time.h>

#include <errno.h>
#include <fenv.h>
#include <math.h>

#define MAX_NIF_NAME_LEN 260
#define FLOAT_BUF_SIZE 64

#define RAISE_ERROR(error_type_atom) \
ctx->x[0] = ERROR_ATOM; \
ctx->x[1] = (error_type_atom); \
return term_invalid_term();

#define RAISE(a, b) \
ctx->x[0] = (a); \
ctx->x[1] = (b); \
Expand Down Expand Up @@ -723,6 +717,18 @@ DEFINE_MATH_NIF(sqrt)
DEFINE_MATH_NIF(tan)
DEFINE_MATH_NIF(tanh)

//Handle optional nifs
#if HAVE_OPEN && HAVE_CLOSE
#define IF_HAVE_OPEN_CLOSE(expr) (expr)
#else
#define IF_HAVE_OPEN_CLOSE(expr) NULL
#endif
#if HAVE_UNLINK
#define IF_HAVE_UNLINK(expr) (expr)
#else
#define IF_HAVE_UNLINK(expr) NULL
#endif

//Ignore warning caused by gperf generated code
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
Expand Down
6 changes: 6 additions & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ atomvm:add_avm_pack_binary/2, &atomvm_add_avm_pack_binary_nif
atomvm:add_avm_pack_file/2, &atomvm_add_avm_pack_file_nif
atomvm:close_avm_pack/2, &atomvm_close_avm_pack_nif
atomvm:read_priv/2, &atomvm_read_priv_nif
atomvm:posix_open/2, IF_HAVE_OPEN_CLOSE(&atomvm_posix_open_nif)
atomvm:posix_open/3, IF_HAVE_OPEN_CLOSE(&atomvm_posix_open_nif)
atomvm:posix_close/1, IF_HAVE_OPEN_CLOSE(&atomvm_posix_close_nif)
atomvm:posix_read/2, IF_HAVE_OPEN_CLOSE(&atomvm_posix_read_nif)
atomvm:posix_write/2, IF_HAVE_OPEN_CLOSE(&atomvm_posix_write_nif)
atomvm:posix_unlink/1, IF_HAVE_UNLINK(&atomvm_posix_unlink_nif)
code:load_abs/1, &code_load_abs_nif
code:load_binary/3, &code_load_binary_nif
console:print/1, &console_print_nif
Expand Down
Loading

0 comments on commit 89700dc

Please sign in to comment.