示例#1
0
文件: prim_file_nif.c 项目: HansN/otp
static ERL_NIF_TERM close_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    enum efile_state_t previous_state;
    efile_data_t *d;

    ASSERT(argc == 1);
    if(!get_file_data(env, argv[0], &d)) {
        return enif_make_badarg(env);
    }

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_CLOSED, EFILE_STATE_IDLE);

    if(previous_state == EFILE_STATE_IDLE) {
        posix_errno_t error;

        enif_demonitor_process(env, d, &d->monitor);

        if(!efile_close(d, &error)) {
            return posix_error_to_tuple(env, error);
        }

        return am_ok;
    } else {
        /* CLOSE_PENDING should be impossible at this point since it requires
         * a transition from BUSY; the only valid state here is CLOSED. */
        ASSERT(previous_state == EFILE_STATE_CLOSED);

        return posix_error_to_tuple(env, EINVAL);
    }
}
示例#2
0
static void owner_death_callback(ErlNifEnv* env, void* obj, ErlNifPid* pid, ErlNifMonitor* mon) {
    efile_data_t *d = (efile_data_t*)obj;

    (void)env;
    (void)pid;
    (void)mon;

    for(;;) {
        enum efile_state_t previous_state;

        previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
            EFILE_STATE_CLOSED, EFILE_STATE_IDLE);

        switch(previous_state) {
        case EFILE_STATE_IDLE:
            efile_close(d);
            return;
        case EFILE_STATE_CLOSE_PENDING:
        case EFILE_STATE_CLOSED:
            /* We're either already closed or managed to mark ourselves for
             * closure in the previous iteration. */
            return;
        case EFILE_STATE_BUSY:
            /* Schedule ourselves to be closed once the current operation
             * finishes, retrying the [IDLE -> CLOSED] transition in case we
             * narrowly passed the [BUSY -> IDLE] one. */
            erts_atomic32_cmpxchg_nob(&d->state,
                EFILE_STATE_CLOSE_PENDING, EFILE_STATE_BUSY);
            break;
        }
    }
}
示例#3
0
文件: prim_file_nif.c 项目: HansN/otp
static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno, ignored;

    efile_fileinfo_t info = {0};
    efile_path_t path;
    efile_data_t *d;

    ErlNifBinary result;

    ASSERT(argc == 1);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_read_info(&path, 1, &info))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_open(&path, EFILE_MODE_READ, efile_resource_type, &d))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    posix_errno = read_file(d, info.size, &result);

    erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
    efile_close(d, &ignored);

    if(posix_errno) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
}
示例#4
0
文件: prim_file_nif.c 项目: HansN/otp
/* This is a special close operation used by the erts_prim_file process for
 * cleaning up orphaned files. It differs from the ordinary close_nif in that
 * it only works for files that have already entered the CLOSED state. */
static ERL_NIF_TERM delayed_close_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t ignored;
    efile_data_t *d;

    ASSERT(argc == 1);
    if(!get_file_data(env, argv[0], &d)) {
        return enif_make_badarg(env);
    }

    ASSERT(erts_atomic32_read_acqb(&d->state) == EFILE_STATE_CLOSED);
    efile_close(d, &ignored);

    return am_ok;
}
示例#5
0
文件: prim_file_nif.c 项目: HansN/otp
static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;
    efile_data_t *d;

    ErlNifPid controlling_process;
    enum efile_modes_t modes;
    ERL_NIF_TERM result;
    efile_path_t path;

    ASSERT(argc == 2);
    if(!enif_is_list(env, argv[1])) {
        return enif_make_badarg(env);
    }

    modes = efile_translate_modelist(env, argv[1]);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_open(&path, modes, efile_resource_type, &d))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    enif_self(env, &controlling_process);

    if(enif_monitor_process(env, d, &controlling_process, &d->monitor)) {
        /* We need to close the file manually as we haven't registered a
         * destructor. */
        posix_errno_t ignored;

        erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
        efile_close(d, &ignored);

        return posix_error_to_tuple(env, EINVAL);
    }

    /* Note that we do not call enif_release_resource at this point. While it's
     * normally safe to leave resource management to the GC, efile_close is a
     * blocking operation which must not be done in the GC callback, and we
     * can't defer it as the resource is gone as soon as it returns.
     *
     * We instead keep the resource alive until efile_close is called, after
     * which it's safe to leave things to the GC. If the controlling process
     * were to die before the user had a chance to close their file, the above
     * monitor will tell the erts_prim_file process to close it for them. */
    result = enif_make_resource(env, d);

    return enif_make_tuple2(env, am_ok, result);
}
示例#6
0
static void gc_callback(ErlNifEnv *env, void* data) {
    efile_data_t *d = (efile_data_t*)data;

    enum efile_state_t previous_state;

    (void)env;

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_CLOSED, EFILE_STATE_IDLE);

    ASSERT(previous_state != EFILE_STATE_CLOSE_PENDING &&
        previous_state != EFILE_STATE_BUSY);

    if(previous_state == EFILE_STATE_IDLE) {
        efile_close(d);
    }
}
示例#7
0
文件: prim_file_nif.c 项目: HansN/otp
static ERL_NIF_TERM file_handle_wrapper(file_op_impl_t operation, ErlNifEnv *env,
        int argc, const ERL_NIF_TERM argv[]) {

    efile_data_t *d;

    enum efile_state_t previous_state;
    ERL_NIF_TERM result;

    if(argc < 1 || !get_file_data(env, argv[0], &d)) {
        return enif_make_badarg(env);
    }

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_BUSY, EFILE_STATE_IDLE);

    if(previous_state == EFILE_STATE_IDLE) {
        result = operation(d, env, argc - 1, &argv[1]);

        previous_state = erts_atomic32_cmpxchg_relb(&d->state,
            EFILE_STATE_IDLE, EFILE_STATE_BUSY);

        ASSERT(previous_state != EFILE_STATE_IDLE);

        if(previous_state == EFILE_STATE_CLOSE_PENDING) {
            /* This is the only point where a change from CLOSE_PENDING is
             * possible, and we're running synchronously, so we can't race with
             * anything else here. */
            posix_errno_t ignored;

            erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
            efile_close(d, &ignored);
        }
    } else {
        /* CLOSE_PENDING should be impossible at this point since it requires
         * a transition from BUSY; the only valid state here is CLOSED. */
        ASSERT(previous_state == EFILE_STATE_CLOSED);

        result = posix_error_to_tuple(env, EINVAL);
    }

    return result;
}
示例#8
0
static ERL_NIF_TERM close_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    enum efile_state_t previous_state;

    ASSERT(argc == 0);

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_CLOSED, EFILE_STATE_BUSY);

    ASSERT(previous_state == EFILE_STATE_CLOSE_PENDING ||
        previous_state == EFILE_STATE_BUSY);

    if(previous_state == EFILE_STATE_BUSY) {
        enif_demonitor_process(env, d, &d->monitor);

        if(!efile_close(d)) {
            return posix_error_to_tuple(env, d->posix_errno);
        }
    }

    return am_ok;
}