Skip to content

Commit

Permalink
chore: introduce atomic integer
Browse files Browse the repository at this point in the history
  • Loading branch information
heywhy committed Jun 10, 2024
1 parent 94321f4 commit 2ec7fc4
Show file tree
Hide file tree
Showing 22 changed files with 537 additions and 129 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@ elasticlunr-*.tar
/priv/plts/*.plt
/priv/plts/*.plt.hash

# CMake
.cmake
Makefile
build/

# Clangd
.cache

compile_flags.txt
32 changes: 32 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.29.0)
project(elasticlunr LANGUAGES CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

if(NOT DEFINED ENV{MIX_APP_PATH})
set(MIX_APP_PATH ${PROJECT_SOURCE_DIR})
else()
set(MIX_APP_PATH $ENV{MIX_APP_PATH})
endif()

if(APPLE)
set(CMAKE_SHARED_LIBRARY_SUFFIX .so)
endif()

if(DEFINED ENV{ERTS_INCLUDE_DIR})
set(ERTS_INCLUDE_DIR $ENV{ERTS_INCLUDE_DIR})
endif()

cmake_path(APPEND PRIV_PATH ${MIX_APP_PATH} "priv")

add_library(nif SHARED c_src/atomic_int.cpp)

target_include_directories(nif SYSTEM PUBLIC ${ERTS_INCLUDE_DIR})
target_link_options(nif PUBLIC -undefined dynamic_lookup -flat_namespace)

add_custom_command(
TARGET nif POST_BUILD
COMMAND mkdir -p ${PRIV_PATH} && cp $<TARGET_FILE:nif> ${PRIV_PATH}
)
33 changes: 0 additions & 33 deletions Makefile

This file was deleted.

144 changes: 144 additions & 0 deletions c_src/atomic_int.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <atomic>
#include <erl_nif.h>

using std::atomic_int64_t;

struct atomic_int {
atomic_int64_t value;

atomic_int(ErlNifSInt64 value) : value{value} {}
atomic_int(atomic_int &) = delete;
atomic_int(atomic_int &&) = delete;
};

static ErlNifResourceType *ATOMICS_RESOURCE_TYPE;

static ERL_NIF_TERM make_atom(ErlNifEnv *env, const char *value);

ERL_NIF_TERM init(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifSInt64 value;
atomic_int **resource = (atomic_int **)enif_alloc_resource(
ATOMICS_RESOURCE_TYPE, sizeof(atomic_int));

if (enif_get_int64(env, argv[0], &value)) {
*resource = new atomic_int{value};
}

ERL_NIF_TERM term = enif_make_resource(env, resource);

enif_release_resource(resource);

return term;
}

ERL_NIF_TERM add(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifUInt64 count;
atomic_int **variable;

if (!enif_get_uint64(env, argv[1], &count)) {
return enif_make_badarg(env);
}

if (enif_get_resource(env, argv[0], ATOMICS_RESOURCE_TYPE,
(void **)&variable)) {
(*variable)->value += count;

return make_atom(env, "ok");
}

return enif_make_badarg(env);
}

ERL_NIF_TERM add_get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifUInt64 count;
atomic_int **variable;

if (!enif_get_uint64(env, argv[1], &count)) {
return enif_make_badarg(env);
}

if (enif_get_resource(env, argv[0], ATOMICS_RESOURCE_TYPE,
(void **)&variable)) {
(*variable)->value += count;

return enif_make_int64(env, (*variable)->value);
}

return enif_make_badarg(env);
}

ERL_NIF_TERM sub_get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifUInt64 count;
atomic_int **variable;

if (!enif_get_uint64(env, argv[1], &count)) {
return enif_make_badarg(env);
}

if (enif_get_resource(env, argv[0], ATOMICS_RESOURCE_TYPE,
(void **)&variable)) {
(*variable)->value += count;

return enif_make_int64(env, (*variable)->value);
}

return enif_make_badarg(env);
}

ERL_NIF_TERM put(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
atomic_int **variable;
ErlNifUInt64 value;

if (!enif_get_uint64(env, argv[1], &value)) {
return enif_make_badarg(env);
}

if (enif_get_resource(env, argv[0], ATOMICS_RESOURCE_TYPE,
(void **)&variable)) {
atomic_int *v = *variable;

v->value.store(value);

return make_atom(env, "ok");
}

return enif_make_badarg(env);
}

ERL_NIF_TERM get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
atomic_int **variable;

if (enif_get_resource(env, argv[0], ATOMICS_RESOURCE_TYPE,
(void **)&variable)) {
return enif_make_int64(env, (*variable)->value);
}

return enif_make_badarg(env);
}

ErlNifFunc nif_funcs[] = {{"init", 1, init}, {"get", 1, get},
{"add", 2, add}, {"put", 2, put},
{"add_get", 2, add_get}, {"sub_get", 2, sub_get}};

void desctructor(ErlNifEnv *env, void *ptr) {
atomic_int **resource = reinterpret_cast<atomic_int **>(ptr);

delete *resource;
}

static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
ATOMICS_RESOURCE_TYPE = enif_open_resource_type(
env, NULL, "atomics.ref", desctructor, ERL_NIF_RT_CREATE, NULL);

return 0;
}

ERL_NIF_INIT(Elixir.Elasticlunr.AtomicInt, nif_funcs, load, NULL, NULL, NULL);

ERL_NIF_TERM make_atom(ErlNifEnv *env, char const *value) {
ERL_NIF_TERM a;

return enif_make_existing_atom(env, value, &a, ERL_NIF_LATIN1)
? a
: enif_make_atom(env, value);
}
1 change: 0 additions & 1 deletion c_src/elasticlunr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <erl_nif.h>
#include <iostream>
#include <map>
#include <string>

using namespace std;

Expand Down
1 change: 1 addition & 0 deletions c_src/index.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "index.hpp"
#include "utils.hpp"
#include <vector>

Index::Index(fs::path const &dir) : dir(dir) { load_from_dir(); };

Expand Down
5 changes: 3 additions & 2 deletions c_src/nif_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#include <map>
#include <string>

using namespace std;

namespace erlang {

using namespace std;

ERL_NIF_TERM badarg(ErlNifEnv *env);
ERL_NIF_TERM atom(ErlNifEnv *env, char const *msg);
ERL_NIF_TERM nil(ErlNifEnv *env);
Expand All @@ -28,6 +28,7 @@ T *resource(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifResourceType *type) {
if (enif_get_resource(env, term, type, (void **)&object)) {
return object;
}

return nullptr;
}

Expand Down
2 changes: 1 addition & 1 deletion c_src/ss_table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#define __SS_TABLE_HPP__

#include <iostream>
#include <string_view>
#include <map>

using namespace std;
Expand Down
6 changes: 4 additions & 2 deletions lib/elasticlunr/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ defmodule Elasticlunr.Application do

use Application

alias Elasticlunr.CompactionController
alias Elasticlunr.PubSub

@impl true
def start(_type, _args) do
children = [
PubSub,
FlakeIdWorker,
CompactionController,
{Registry, name: Elasticlunr.Fs, keys: :unique},
{Registry, name: Elasticlunr.IndexRegistry, keys: :unique},
{Task.Supervisor, name: Elasticlunr.BackgroundTaskSupervisor},
FlakeIdWorker
{Task.Supervisor, name: Elasticlunr.BackgroundTaskSupervisor}
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
36 changes: 36 additions & 0 deletions lib/elasticlunr/atomic_int.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule Elasticlunr.AtomicInt do
@on_load :load

@type t :: reference()

def load do
nif_file = ~c"#{:code.priv_dir(:elasticlunr)}/libnif"

case :erlang.load_nif(nif_file, 0) do
:ok -> :ok
{:error, {:reload, _}} -> :ok
{:error, reason} -> IO.puts("Failed to load nif: #{reason}")
end
end

@spec new(integer()) :: t()
def new(value \\ 0), do: init(value)

# nif methods
def init(_value), do: :erlang.nif_error(:not_loaded)

@spec get(t()) :: integer()
def get(_ref), do: :erlang.nif_error(:not_loaded)

@spec put(t(), integer()) :: :ok
def put(_ref, _value), do: :erlang.nif_error(:not_loaded)

@spec add(t(), non_neg_integer()) :: :ok
def add(_ref, _incr), do: :erlang.nif_error(:not_loaded)

@spec add_get(t(), non_neg_integer()) :: integer()
def add_get(_ref, _incr), do: :erlang.nif_error(:not_loaded)

@spec sub_get(t(), non_neg_integer()) :: integer()
def sub_get(_ref, _incr), do: :erlang.nif_error(:not_loaded)
end
22 changes: 8 additions & 14 deletions lib/elasticlunr/compaction.ex
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
defmodule Elasticlunr.Compaction do
use GenServer
alias Elasticlunr.FileMeta
alias Elasticlunr.Manifest.Changes

defstruct [:strategy, :task, :watcher]
@enforce_keys [:level]
defstruct [:level, inputs: [], parent_inputs: [], changes: %Changes{}]

@type t :: %__MODULE__{
strategy: tuple(),
task: nil | Task.t()
level: non_neg_integer(),
changes: Changes.t(),
inputs: [FileMeta.t()],
parent_inputs: [FileMeta.t()]
}

@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, hibernate_after: 5_000)
end

@impl true
def init(_opts) do
{:ok, %__MODULE__{}}
end
end
37 changes: 37 additions & 0 deletions lib/elasticlunr/compaction_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule Elasticlunr.CompactionController do
use GenServer

alias Elasticlunr.Compaction

defstruct [:task, compactions: []]

@spec process(Compaction.t(), Path.t()) :: :ok
def process(%Compaction{} = compaction, dir) do
GenServer.call(__MODULE__, {:process, compaction, dir})
end

@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__, hibernate_after: 5_000)
end

@impl true
def init([]), do: {:ok, %__MODULE__{}}

@impl true
def handle_call(
{:process, compaction, dir},
{from, _tag},
%__MODULE__{compactions: compactions, task: task} = state
) do
compactions = [{compaction, dir, from}] ++ compactions

task =
case task do
nil -> nil
task -> task
end

{:reply, :ok, %{state | compactions: compactions, task: task}}
end
end
Loading

0 comments on commit 2ec7fc4

Please sign in to comment.