Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Implement open/close/pread/pwrite

open option 'direct' makes it use so-called
resource binaries which keep the memory
mapped until the last reference is gone.
  • Loading branch information...
commit 63c5cc7e5a234926bbe0ce903b75aafadb58e80c 1 parent 78b52ae
@krestenkrab authored
View
6 Makefile
@@ -0,0 +1,6 @@
+
+all:
+ ./rebar compile eunit
+
+clean:
+ ./rebar clean
View
355 c_src/emmap.cpp
@@ -0,0 +1,355 @@
+
+
+#include "erl_nif_compat.h"
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+static ErlNifResourceType* MMAP_RESOURCE;
+
+typedef struct
+{
+ int direct;
+ int prot;
+ bool closed;
+ ErlNifRWLock* rwlock;
+ void* mem;
+ size_t len;
+} mhandle;
+
+static int on_load(ErlNifEnv*, void**, ERL_NIF_TERM);
+
+static int emmap_unmap(mhandle *handle, bool from_dtor)
+{
+ if (handle->mem != 0) {
+ if (from_dtor || !handle->direct) {
+ int result = munmap(handle->mem, handle->len);
+ handle->mem = 0;
+ return result;
+ } else {
+ handle->closed = true;
+ }
+ }
+ return 0;
+}
+
+void emmap_dtor(ErlNifEnv* env, void* arg)
+{
+ mhandle* handle = (mhandle*)arg;
+ emmap_unmap(handle, true);
+
+ // only the destructor destroys the rwlock
+ enif_rwlock_destroy(handle->rwlock);
+}
+
+static ERL_NIF_TERM ATOM_OK;
+static ERL_NIF_TERM ATOM_TRUE;
+static ERL_NIF_TERM ATOM_FALSE;
+static ERL_NIF_TERM ATOM_ERROR;
+static ERL_NIF_TERM ATOM_DIRECT;
+static ERL_NIF_TERM ATOM_READ;
+static ERL_NIF_TERM ATOM_WRITE;
+static ERL_NIF_TERM ATOM_NONE;
+static ERL_NIF_TERM ATOM_PRIVATE;
+static ERL_NIF_TERM ATOM_SHARED;
+static ERL_NIF_TERM ATOM_ANON;
+static ERL_NIF_TERM ATOM_FILE;
+static ERL_NIF_TERM ATOM_FIXED;
+static ERL_NIF_TERM ATOM_NOCACHE;
+
+static ERL_NIF_TERM emmap_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM emmap_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM emmap_pread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM emmap_pwrite(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+extern "C" {
+
+ static ErlNifFunc nif_funcs[] =
+ {
+ {"open", 4, emmap_open},
+ {"close", 1, emmap_close},
+ {"pread", 3, emmap_pread},
+ {"pwrite", 3, emmap_pwrite},
+ };
+
+ ERL_NIF_INIT(emmap, nif_funcs, &on_load, NULL, NULL, NULL);
+};
+
+static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER);
+ MMAP_RESOURCE = enif_open_resource_type_compat(env, "mmap_resource", &emmap_dtor, flags, 0);
+
+ ATOM_OK = enif_make_atom(env, "ok");
+ ATOM_TRUE = enif_make_atom(env, "true");
+ ATOM_FALSE = enif_make_atom(env, "false");
+ ATOM_ERROR = enif_make_atom(env, "error");
+
+ ATOM_DIRECT = enif_make_atom(env, "direct");
+ ATOM_READ = enif_make_atom(env, "read");
+ ATOM_WRITE = enif_make_atom(env, "write");
+ ATOM_NONE = enif_make_atom(env, "none");
+ ATOM_PRIVATE = enif_make_atom(env, "private");
+ ATOM_SHARED = enif_make_atom(env, "shared");
+ ATOM_ANON = enif_make_atom(env, "anon");
+ ATOM_FILE = enif_make_atom(env, "file");
+ ATOM_FIXED = enif_make_atom(env, "fixed");
+ ATOM_NOCACHE = enif_make_atom(env, "nocache");
+
+ return 0;
+}
+
+static ERL_NIF_TERM describe_error(ErlNifEnv* env, int err) {
+ switch (err) {
+ case EAGAIN:
+ return enif_make_atom(env, "eagain");
+ case EINVAL:
+ return enif_make_atom(env, "einval");
+ case ENOSPC:
+ return enif_make_atom(env, "enospc");
+ case ENOENT:
+ return enif_make_atom(env, "enoent");
+ case ENOMEM:
+ return enif_make_atom(env, "enomem");
+ case EACCES:
+ return enif_make_atom(env, "eacces");
+ case EBADF:
+ return enif_make_atom(env, "ebadf");
+ case ENODEV:
+ return enif_make_atom(env, "enodev");
+ case ENXIO:
+ return enif_make_atom(env, "enxio");
+ case EOVERFLOW:
+ return enif_make_atom(env, "eoverflow");
+ }
+ return enif_make_tuple2(env,
+ enif_make_atom(env, "errno"),
+ enif_make_int(env, err));
+}
+
+static ERL_NIF_TERM make_error_tuple(ErlNifEnv* env, int err) {
+ return enif_make_tuple2(env, ATOM_ERROR, describe_error(env, err));
+}
+
+
+int decode_flags(ErlNifEnv* env, ERL_NIF_TERM list, int *prot, int *flags, bool *direct)
+{
+ bool d = false;
+ int f = MAP_FILE;
+ int p = 0;
+ ERL_NIF_TERM head;
+ while (enif_get_list_cell(env, list, &head, &list)) {
+
+ if (enif_is_identical(head, ATOM_READ)) {
+ p |= PROT_READ;
+ } else if (enif_is_identical(head, ATOM_DIRECT)) {
+ d = true;
+ } else if (enif_is_identical(head, ATOM_WRITE)) {
+ p |= PROT_WRITE;
+// } else if (enif_is_identical(head, ATOM_NONE)) {
+// p |= PROT_NONE;
+
+ } else if (enif_is_identical(head, ATOM_PRIVATE)) {
+ f |= MAP_PRIVATE;
+ } else if (enif_is_identical(head, ATOM_SHARED)) {
+ f |= MAP_SHARED;
+// } else if (enif_is_identical(head, ATOM_ANON)) {
+// f |= MAP_ANON;
+// } else if (enif_is_identical(head, ATOM_FILE)) {
+// f |= MAP_FILE;
+// } else if (enif_is_identical(head, ATOM_FIXED)) {
+// f |= MAP_FIXED;
+ } else if (enif_is_identical(head, ATOM_NOCACHE)) {
+ f |= MAP_NOCACHE;
+
+ } else {
+ return 0;
+ }
+ }
+
+ // direct cannot be write
+ if (d & ((p & PROT_WRITE) != 0))
+ return 0;
+
+ // default to private
+ if ((f & (MAP_SHARED|MAP_PRIVATE)) == 0)
+ f |= MAP_PRIVATE;
+
+ // default to read-only
+ if ((p & (PROT_READ|PROT_WRITE)) == 0)
+ p |= PROT_READ;
+
+ *flags = f;
+ *prot = p;
+ *direct = d;
+
+ return 1;
+}
+
+static ERL_NIF_TERM emmap_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int flags;
+ int prot;
+ bool direct;
+ unsigned long int len;
+ unsigned long int offset;
+ char buf[1024];
+
+#ifndef NDEBUG
+ if ( sizeof(long int) != sizeof(size_t) ) {
+ abort();
+ }
+#endif
+
+ if (argc == 4
+ && enif_get_string(env, argv[0], buf, 1024, ERL_NIF_LATIN1)
+ && enif_get_ulong(env, argv[1], &offset)
+ && enif_get_ulong(env, argv[2], &len)
+ && decode_flags(env, argv[3], &prot, &flags, &direct)) {
+
+ int mode = (((prot & PROT_WRITE)==PROT_WRITE) ? O_RDWR : O_RDONLY);
+
+ int fd = open(buf, mode);
+ if (fd < 0) {
+ return make_error_tuple(env, errno);
+ }
+
+ void * res = mmap(0, (size_t) len, prot, flags, fd, (size_t) offset);
+ if (res == MAP_FAILED) {
+ return make_error_tuple(env, errno);
+ }
+
+ close(fd);
+
+ mhandle* handle = (mhandle*)enif_alloc_resource_compat(env, MMAP_RESOURCE,
+ sizeof(mhandle));
+
+ handle->rwlock = enif_rwlock_create((char*)"mmap");
+ handle->prot = prot;
+ handle->mem = res;
+ handle->len = len;
+ handle->closed = false;
+ handle->direct = direct;
+
+ ERL_NIF_TERM resource = enif_make_resource(env, handle);
+ enif_release_resource_compat(env, handle);
+
+ return enif_make_tuple2(env,
+ enif_make_atom(env, "ok"),
+ resource);
+
+ } else {
+ return enif_make_badarg(env);
+ }
+}
+
+static ERL_NIF_TERM emmap_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ mhandle *handle;
+ if (argc==1 && enif_get_resource(env, argv[0], MMAP_RESOURCE, (void**)&handle))
+ {
+ int res;
+ enif_rwlock_rwlock(handle->rwlock);
+ res = emmap_unmap(handle, false);
+ enif_rwlock_rwunlock(handle->rwlock);
+
+ if (res == 0) {
+ return ATOM_OK;
+ }
+
+ return make_error_tuple(env, errno);
+ }
+ else
+ {
+ return enif_make_badarg(env);
+ }
+}
+
+static ERL_NIF_TERM emmap_pread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ unsigned long pos, bytes;
+ mhandle *handle;
+ if (argc==3
+ && enif_get_resource(env, argv[0], MMAP_RESOURCE, (void**)&handle)
+ && enif_get_ulong(env, argv[1], &pos)
+ && enif_get_ulong(env, argv[2], &bytes)
+ && pos >= 0
+ && bytes >= 0
+ && (pos + bytes) <= handle->len
+ )
+ {
+ ErlNifBinary bin;
+
+ if ((handle->prot & PROT_READ) == 0) {
+ return make_error_tuple(env, EACCES);
+ }
+
+ // if this mmap is direct, use a resource binary
+ if (handle->direct) {
+
+ ERL_NIF_TERM res = enif_make_resource_binary
+ (env, handle, (void*) (((char*)handle->mem) + pos), bytes);
+
+ return enif_make_tuple2(env, ATOM_OK, res);
+
+ } else {
+
+ // When it is non-direct, we have to allocate the binary
+ if (!enif_alloc_binary((size_t) bytes, &bin)) {
+ return make_error_tuple(env, ENOMEM);
+ }
+
+ enif_rwlock_rlock(handle->rwlock);
+ if (handle->closed) {
+ enif_rwlock_runlock(handle->rwlock);
+ return enif_make_badarg(env);
+ }
+ memcpy(bin.data, (void*) (((char*)handle->mem) + pos), bytes);
+ enif_rwlock_runlock(handle->rwlock);
+
+ ERL_NIF_TERM res = enif_make_binary(env, &bin);
+ return enif_make_tuple2(env, ATOM_OK, res);
+ }
+ }
+ else
+ {
+ return enif_make_badarg(env);
+ }
+}
+
+static ERL_NIF_TERM emmap_pwrite(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+ unsigned long pos;
+ mhandle *handle;
+ if (argc==3
+ && enif_get_resource(env, argv[0], MMAP_RESOURCE, (void**)&handle)
+ && enif_get_ulong(env, argv[1], &pos)
+ && enif_inspect_binary(env, argv[2], &bin)
+ && pos >= 0
+ && (pos + bin.size) <= handle->len
+ )
+ {
+
+ if ((handle->prot & PROT_WRITE) == 0) {
+ return make_error_tuple(env, EACCES);
+ }
+
+ enif_rwlock_rwlock(handle->rwlock);
+ if (handle->closed) {
+ enif_rwlock_rwunlock(handle->rwlock);
+ return enif_make_badarg(env);
+ } else {
+ memcpy((void*) (((char*)handle->mem) + pos), bin.data, bin.size);
+ enif_rwlock_rwunlock(handle->rwlock);
+ }
+
+ return ATOM_OK;
+ }
+ else
+ {
+ return enif_make_badarg(env);
+ }
+}
View
71 c_src/erl_nif_compat.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2010-2011 Basho Technologies, Inc.
+ *
+ * This file is provided to you 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.
+*/
+
+#ifndef ERL_NIF_COMPAT_H_
+#define ERL_NIF_COMPAT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "erl_nif.h"
+
+#if ERL_NIF_MAJOR_VERSION == 1 && ERL_NIF_MINOR_VERSION == 0
+
+#define enif_open_resource_type_compat enif_open_resource_type
+#define enif_alloc_resource_compat enif_alloc_resource
+#define enif_release_resource_compat enif_release_resource
+#define enif_alloc_binary_compat enif_alloc_binary
+#define enif_alloc_compat enif_alloc
+#define enif_free_compat enif_free
+#define enif_cond_create erl_drv_cond_create
+#define enif_cond_destroy erl_drv_cond_destroy
+#define enif_cond_signal erl_drv_cond_signal
+#define enif_cond_broadcast erl_drv_cond_broadcast
+#define enif_cond_wait erl_drv_cond_wait
+#define ErlNifCond ErlDrvCond
+#endif /* R13B04 */
+
+#if ERL_NIF_MAJOR_VERSION == 2 && ERL_NIF_MINOR_VERSION >= 0
+
+#define enif_open_resource_type_compat(E, N, D, F, T) \
+ enif_open_resource_type(E, NULL, N, D, F, T)
+
+#define enif_alloc_resource_compat(E, T, S) \
+ enif_alloc_resource(T, S)
+
+#define enif_release_resource_compat(E, H) \
+ enif_release_resource(H)
+
+#define enif_alloc_binary_compat(E, S, B) \
+ enif_alloc_binary(S, B)
+
+#define enif_alloc_compat(E, S) \
+ enif_alloc(S)
+
+#define enif_free_compat(E, P) \
+ enif_free(P)
+
+#endif /* R14 */
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ERL_NIF_COMPAT_H_ */
View
14 ebin/emmap.app
@@ -0,0 +1,14 @@
+{application, emmap,
+ [
+ {description, ""},
+ {vsn, "1.0.0"},
+ {modules, [
+ emmap
+ ]},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]},
+ {env, []}
+ ]}.
View
BIN  rebar
Binary file not shown
View
10 rebar.config
@@ -0,0 +1,10 @@
+{port_sources, ["c_src/*.cpp"]}.
+{so_name, "emmap_nifs.so"}.
+
+{port_envs, [
+ %% Make sure to link -lstdc++
+ {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin)",
+ "LDFLAGS", "$LDFLAGS -lstdc++"},
+ {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin)",
+ "CFLAGS", "$CFLAGS -DNDEBUG"}
+ ]}.
View
60 src/emmap.erl
@@ -0,0 +1,60 @@
+-module(emmap).
+
+-export([open/4, close/1, pread/3, pwrite/3]).
+-on_load(init/0).
+
+-ifdef(TEST).
+-export([simple_test/0]).
+-endif.
+
+-include_lib("kernel/include/file.hrl").
+
+init() ->
+ case code:priv_dir(emmap) of
+ {error, bad_name} ->
+ case code:which(?MODULE) of
+ Filename when is_list(Filename) ->
+ SoName = filename:join([filename:dirname(Filename),"../priv", "emmap_nifs"]);
+ _ ->
+ SoName = filename:join("../priv", "emmap_nifs")
+ end;
+ Dir ->
+ SoName = filename:join(Dir, "emmap_nifs")
+ end,
+ erlang:load_nif(SoName, 0).
+
+
+close(_Ref) ->
+ ok.
+
+-spec open(File::string(),
+ Offset::pos_integer(),
+ Length::pos_integer(),
+ Options::[ read|write|direct|nocache|private|shared ]) ->
+ {ok, term()} | {error, term()}.
+
+open(_FileName,_Off,_Len,_Options) ->
+ <<>>.
+
+pread(_File,_Off,_Len) ->
+ {ok, <<>>}.
+
+pwrite(_File,_Off,_Data) ->
+ ok.
+
+
+
+-ifdef(TEST).
+
+simple_test() ->
+ {ok, File} = file:open("test.data", [raw, write]),
+ ok = file:write(File, <<"abcd">>),
+ ok = file:close(File),
+
+ {ok, MFile} = emmap:open("test.data", 0, 4, [direct]),
+ {ok, <<"cd">>} = emmap:pread(MFile, 2, 2),
+ {error, eacces} = emmap:pwrite(MFile, 2, <<"xx">>),
+ ok = emmap:close(MFile).
+
+
+-endif.
Please sign in to comment.
Something went wrong with that request. Please try again.