diff --git a/.gitignore b/.gitignore index 49583b83..3d25e187 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ rebar3.crashdump _* .eunit *.o +*.so *.beam *.plt *.swp diff --git a/c_src/Makefile b/c_src/Makefile new file mode 100644 index 00000000..6cff0714 --- /dev/null +++ b/c_src/Makefile @@ -0,0 +1,82 @@ +# Based on c_src.mk from erlang.mk by Loic Hoguin + +CURDIR := $(shell pwd) +BASEDIR := $(abspath $(CURDIR)/..) + +PROJECT := prometheus_nif + +ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s/erts-~s/include/\", [code:root_dir(), erlang:system_info(version)]).") +ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, include)]).") +ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, lib)]).") + +C_SRC_DIR = $(CURDIR) +C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so + +# System type and C compiler/flags. + +UNAME_SYS := $(shell uname -s) +ifeq ($(UNAME_SYS), Darwin) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -arch x86_64 -std=c++11 -finline-functions -Wall + LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress +else ifeq ($(UNAME_SYS), FreeBSD) + CC ?= clang + CXX ?= clang++ + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -std=c++11 -finline-functions -Wall +else ifeq ($(UNAME_SYS), Linux) + CC ?= gcc + CXX ?= g++ + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -std=c++11 -finline-functions -Wall +endif + +CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) +CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) -std=c++11 + +LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei + +ifeq ($(UNAME_SYS), OpenBSD) + LDLIBS += -lestdc++ +else + LDLIBS += -lstdc++ +endif + +LDFLAGS += -shared + +# Verbosity. + +c_verbose_0 = @echo " C " $(?F); +c_verbose = $(c_verbose_$(V)) + +cpp_verbose_0 = @echo " CPP " $(?F); +cpp_verbose = $(cpp_verbose_$(V)) + +link_verbose_0 = @echo " LD " $(@F); +link_verbose = $(link_verbose_$(V)) + +SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) +OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) + +COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c +COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c + +$(C_SRC_OUTPUT): $(OBJECTS) + @mkdir -p $(BASEDIR)/priv/ + $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) + +%.o: %.c + $(COMPILE_C) $(OUTPUT_OPTION) $< + +%.o: %.cc + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.C + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.cpp + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +clean: + @rm -f $(C_SRC_OUTPUT) $(OBJECTS) diff --git a/c_src/atoms.h b/c_src/atoms.h new file mode 100644 index 00000000..1dab97d2 --- /dev/null +++ b/c_src/atoms.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2017, Benoit Chesneau . + * + * This file is part of instrument released under the MIT license. + * See the NOTICE for more information. + */ + +#ifndef ATOMS_H +#define ATOMS_H + +namespace prometheus { + extern ERL_NIF_TERM ATOM_OK; + extern ERL_NIF_TERM ATOM_ERROR; + extern ERL_NIF_TERM ATOM_EINVAL; + extern ERL_NIF_TERM ATOM_BADARG; + +} // namespace prometheus + + +#endif // ATOMS_H diff --git a/c_src/prometheus_nif.cc b/c_src/prometheus_nif.cc new file mode 100644 index 00000000..8632d231 --- /dev/null +++ b/c_src/prometheus_nif.cc @@ -0,0 +1,195 @@ +/* Copyright (c) 2017, Benoit Chesneau . + * + * This file is part of prometheus released under the MIT license. + * See the NOTICE for more information. + */ + +#include + +#include +#include + +#include "prometheus_nif.h" + +#include "erl_nif.h" + +#ifndef ATOMS_H +#include "atoms.h" +#endif + +#include "value.h" + + +static ErlNifFunc nif_funcs[] = +{ + {"new_value", 0, prometheus::NewValue}, + {"inc_value", 1, prometheus::IncValue}, + {"inc_value", 2, prometheus::IncValue}, + {"dec_value", 1, prometheus::DecValue}, + {"dec_value", 2, prometheus::DecValue}, + {"set_value", 2, prometheus::SetValue}, + {"get_value", 1, prometheus::GetValue} +}; + +namespace prometheus { + + ERL_NIF_TERM ATOM_OK; + ERL_NIF_TERM ATOM_ERROR; + ERL_NIF_TERM ATOM_EINVAL; + ERL_NIF_TERM ATOM_BADARG; + + + ErlNifResourceType *m_Value_RESOURCE; + + void + value_resource_cleanup(ErlNifEnv *env, void *res) + { + + } + + void + CreateValueType(ErlNifEnv *env) + { + ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); + m_Value_RESOURCE = enif_open_resource_type(env, NULL, "prometheus_Value", value_resource_cleanup, flags, NULL); + return; + } + + ERL_NIF_TERM + NewValue( + ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) + { + prometheus::Value * value; + void *alloc_ptr; + alloc_ptr = enif_alloc_resource(m_Value_RESOURCE, sizeof(prometheus::Value)); + value = new(alloc_ptr) prometheus::Value(); + ERL_NIF_TERM result = enif_make_resource(env, value); + enif_release_resource(value); + return enif_make_tuple2(env, ATOM_OK, result); + } + + ERL_NIF_TERM + IncValue( + ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) + { + prometheus::Value* value_ptr; + + if(!enif_get_resource(env, argv[0], m_Value_RESOURCE, (void **) &value_ptr)) + return enif_make_badarg(env); + + if(argc > 1) + { + double v; + if (!enif_get_double(env, argv[1], &v)) + return enif_make_badarg(env); + value_ptr->Increment(v); + } + else + { + value_ptr->Increment(); + } + + return ATOM_OK; + } + + ERL_NIF_TERM + DecValue( + ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) + { + prometheus::Value* value_ptr; + + if(!enif_get_resource(env, argv[0], m_Value_RESOURCE, (void **) &value_ptr)) + return enif_make_badarg(env); + + if(argc > 1) + { + double v; + if (!enif_get_double(env, argv[1], &v)) + return enif_make_badarg(env); + value_ptr->Decrement(v); + } + else + { + value_ptr->Decrement(); + } + + return ATOM_OK; + } + + ERL_NIF_TERM + SetValue( + ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) + { + prometheus::Value* value_ptr; + + if(!enif_get_resource(env, argv[0], m_Value_RESOURCE, (void **) &value_ptr)) + return enif_make_badarg(env); + + double v; + if (!enif_get_double(env, argv[1], &v)) + return enif_make_badarg(env); + value_ptr->Set(v); + + return ATOM_OK; + } + + ERL_NIF_TERM + GetValue( + ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) + { + prometheus::Value* value_ptr; + + if(!enif_get_resource(env, argv[0], m_Value_RESOURCE, (void **) &value_ptr)) + return enif_make_badarg(env); + + double v; + v = value_ptr->GetValue(); + return enif_make_double(env, v); + } + +} // namespace prometheus + + +/*nif initialization */ + +static void on_unload(ErlNifEnv *env, void *priv_data) +{ +} + + +static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + try + { + prometheus::CreateValueType(env); + +#define ATOM(Id, Value) { Id = enif_make_atom(env, Value); } + // initialize the atoms + ATOM(prometheus::ATOM_OK, "ok"); + ATOM(prometheus::ATOM_ERROR, "error"); + ATOM(prometheus::ATOM_EINVAL, "einval"); + ATOM(prometheus::ATOM_BADARG, "badarg"); +#undef ATOM + + return 0; + + } + catch(...) + { + return -1; + } +} + +extern "C" { + ERL_NIF_INIT(prometheus_value, nif_funcs, &on_load, NULL, NULL, &on_unload); +} diff --git a/c_src/prometheus_nif.h b/c_src/prometheus_nif.h new file mode 100644 index 00000000..26973a80 --- /dev/null +++ b/c_src/prometheus_nif.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2017, Benoit Chesneau . + * + * This file is part of instrument released under the MIT license. + * See the NOTICE for more information. + */ + + +#ifndef INCL_instrument_H +#define INCL_instrument_H + +extern "C" { +#include "erl_nif.h" + +ERL_NIF_TERM instrument_new_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM instrument_inc_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM instrument_dec_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM instrument_set_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM instrument_get_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +} + +namespace prometheus { + +ERL_NIF_TERM NewValue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM IncValue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM DecValue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM SetValue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM GetValue(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +} // namespace instrument + +#endif diff --git a/c_src/value.cc b/c_src/value.cc new file mode 100644 index 00000000..9bfe8612 --- /dev/null +++ b/c_src/value.cc @@ -0,0 +1,42 @@ +/* Copyright (c) 2017, Benoit Chesneau . + * + * This file is part of instrument released under the MIT license. + * See the NOTICE for more information. + */ + +#include "value.h" + +namespace prometheus { + +Value::Value() : value_{0} {} + +Value::Value(double value) : value_{value} {} + +void Value::Increment() { Increment(1.0); } +void Value::Increment(double value) { + if (value < 0.0) { + return; + } + Change(value); +} + +void Value::Decrement() { Decrement(1.0); } + +void Value::Decrement(double value) { + if (value < 0.0) { + return; + } + Change(-1.0 * value); +} + +void Value::Set(double value) { value_.store(value); } + +void Value::Change(double value) { + auto current = value_.load(); + while (!value_.compare_exchange_weak(current, current + value)) + ; +} + +double Value::GetValue() const { return value_; } + +} diff --git a/c_src/value.h b/c_src/value.h new file mode 100644 index 00000000..128ef49f --- /dev/null +++ b/c_src/value.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2017, Benoit Chesneau . + * + * This file is part of instrument released under the MIT license. + * See the NOTICE for more information. + */ + +#pragma once + +#include + +namespace prometheus { + +class Value { + public: + Value(); + Value(double); + + void Increment(); + void Increment(double); + void Decrement(); + void Decrement(double); + void Set(double); + double GetValue() const; + + private: + void Change(double); + mutable std::atomic value_; +}; + +} diff --git a/rebar.config b/rebar.config index 6e88e819..2aed1740 100644 --- a/rebar.config +++ b/rebar.config @@ -28,6 +28,14 @@ {xref_checks,[undefined_function_calls,undefined_functions,locals_not_used, deprecated_function_calls,deprecated_functions]}. +{pre_hooks, + [{"(linux|darwin|solaris)", compile, "make -C c_src"}, + {"(freebsd)", compile, "gmake -C c_src"}]}. + +{post_hooks, + [{"(linux|darwin|solaris)", clean, "make -C c_src clean"}, + {"(freebsd)", clean, "gmake -C c_src clean"}]}. + {profiles, [{docs, [{deps, [{edown, "0.8.1"}]}, {edoc_opts, [{doclet, edown_doclet},