├── .gitignore ├── Makefile ├── README.md ├── apps ├── dirty_sched │ ├── c_src │ │ ├── dirty_sched.c │ │ ├── dirty_sched_using_macros.c │ │ └── nif_util.h │ ├── rebar.config │ ├── src │ │ ├── dirty_sched.app.src │ │ └── dirty_sched.erl │ └── test │ │ └── dirty_sched_tests.erl ├── resources │ ├── c_src │ │ └── resources.c │ ├── rebar.config │ ├── src │ │ ├── resources.app.src │ │ └── resources.erl │ └── test │ │ └── resource_tests.erl ├── skeleton │ ├── c_src │ │ └── skeleton.c │ ├── rebar.config │ ├── src │ │ ├── skeleton.app.src │ │ └── skeleton.erl │ └── test │ │ └── skeleton_tests.erl ├── termsend │ ├── c_src │ │ └── termsend.c │ ├── rebar.config │ ├── src │ │ ├── termsend.app.src │ │ └── termsend.erl │ └── test │ │ └── termsend_tests.erl └── termsend_threaded │ ├── c_src │ └── termsend_threaded.c │ ├── rebar.config │ ├── src │ ├── termsend_threaded.app.src │ └── termsend_threaded.erl │ └── test │ └── termsend_threaded_tests.erl ├── rebar ├── rebar.config └── shell /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.beam 4 | 5 | .eunit/ 6 | ebin/ 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | ./rebar compile 4 | 5 | clean: 6 | ./rebar clean 7 | rm -rf appps/*/priv/*.so 8 | rm -rf apps/*/.eunit/ 9 | 10 | check: 11 | ./rebar compile eunit 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NIF Examples 2 | ============ 3 | 4 | Some code for examples of using NIFs. 5 | 6 | All examples are released into the public domain. 7 | -------------------------------------------------------------------------------- /apps/dirty_sched/c_src/dirty_sched.c: -------------------------------------------------------------------------------- 1 | #include "erl_nif.h" 2 | 3 | #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT 4 | 5 | static ERL_NIF_TERM 6 | double_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 7 | { 8 | ERL_NIF_TERM r; 9 | int v; 10 | enif_get_int(env, argv[0], &v); 11 | r = enif_make_int(env, v * 2); 12 | return enif_schedule_dirty_nif_finalizer(env, r, enif_dirty_nif_finalizer); 13 | } 14 | 15 | 16 | static ERL_NIF_TERM 17 | double_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 18 | { 19 | int flags = ERL_NIF_DIRTY_JOB_CPU_BOUND; 20 | return enif_schedule_dirty_nif(env, flags, double_run, argc, argv); 21 | } 22 | 23 | 24 | static ERL_NIF_TERM 25 | triple_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 26 | { 27 | ERL_NIF_TERM r; 28 | int v; 29 | 30 | enif_get_int(env, argv[0], &v); 31 | r = enif_make_int(env, v * 3); 32 | 33 | if(enif_is_on_dirty_scheduler(env)) { 34 | return enif_schedule_dirty_nif_finalizer( 35 | env, 36 | r, 37 | enif_dirty_nif_finalizer 38 | ); 39 | } else { 40 | return r; 41 | } 42 | } 43 | 44 | 45 | static ERL_NIF_TERM 46 | triple_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 47 | { 48 | int flags = ERL_NIF_DIRTY_JOB_CPU_BOUND; 49 | return enif_schedule_dirty_nif(env, flags, triple_run, argc, argv); 50 | } 51 | 52 | 53 | static ErlNifFunc nif_funcs[] = { 54 | {"double", 1, double_nif}, 55 | {"triple_defer", 1, triple_nif}, 56 | {"triple_direct", 1, triple_run} 57 | }; 58 | 59 | 60 | #else // No dirty schedulers 61 | 62 | 63 | static ERL_NIF_TERM 64 | no_dirty(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 65 | { 66 | return enif_make_tuple2( 67 | enif_make_atom(env, "error"), 68 | enif_make_atom(env, "no_dirty_schedulers") 69 | ); 70 | } 71 | 72 | 73 | static ErlNifFunc nif_funcs[] = { 74 | {"double", 1, no_dirty}, 75 | {"triple_defer", 1, no_dirty}, 76 | {"triple_direct", 1, no_dirty} 77 | }; 78 | 79 | 80 | #endif 81 | 82 | 83 | ERL_NIF_INIT(dirty_sched, nif_funcs, NULL, NULL, NULL, NULL); 84 | -------------------------------------------------------------------------------- /apps/dirty_sched/c_src/dirty_sched_using_macros.c: -------------------------------------------------------------------------------- 1 | #include "erl_nif.h" 2 | 3 | #include "nif_util.h" 4 | 5 | 6 | static ERL_NIF_TERM 7 | double_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 8 | { 9 | ERL_NIF_TERM r; 10 | int v; 11 | 12 | enif_get_int(env, argv[0], &v); 13 | r = enif_make_int(env, v * 2); 14 | 15 | ERL_NIF_RETURN(r); 16 | } 17 | 18 | 19 | static ERL_NIF_TERM 20 | triple_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 21 | { 22 | 23 | ERL_NIF_TERM r; 24 | int v; 25 | 26 | enif_get_int(env, argv[0], &v); 27 | r = enif_make_int(env, v * 3); 28 | 29 | ERL_NIF_RETURN(r); 30 | } 31 | 32 | 33 | ERL_NIF_WRAP_DIRTY_CPU(double_run) 34 | ERL_NIF_WRAP_DIRTY_CPU(triple_run) 35 | 36 | 37 | static ErlNifFunc nif_funcs[] = { 38 | {"double", 1, ERL_NIF_WRAPPED(double_run)}, 39 | {"triple_defer", 1, ERL_NIF_WRAPPED(triple_run)}, 40 | {"triple_direct", 1, triple_run} 41 | }; 42 | 43 | 44 | ERL_NIF_INIT(dirty_sched, nif_funcs, NULL, NULL, NULL, NULL); 45 | -------------------------------------------------------------------------------- /apps/dirty_sched/c_src/nif_util.h: -------------------------------------------------------------------------------- 1 | #ifndef NIF_UTIL_H 2 | #define NIF_UTIL_H 3 | 4 | #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT 5 | 6 | #define ERL_NIF_WRAP_DIRTY(name, flags) \ 7 | static ERL_NIF_TERM \ 8 | nif_##name(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) \ 9 | { \ 10 | return enif_schedule_dirty_nif(env, (flags), name, argc, argv); \ 11 | } 12 | 13 | 14 | #define ERL_NIF_WRAPPED(name) nif_##name 15 | 16 | 17 | #define ERL_NIF_RETURN(value) \ 18 | do { \ 19 | if(enif_is_on_dirty_scheduler(env)) { \ 20 | return enif_schedule_dirty_nif_finalizer( \ 21 | env, \ 22 | value, \ 23 | enif_dirty_nif_finalizer \ 24 | ); \ 25 | } else { \ 26 | return value; \ 27 | } \ 28 | } while(0) 29 | 30 | 31 | #else 32 | 33 | 34 | #define ERL_NIF_WRAP_DIRTY(name, flags) 35 | #define ERL_NIF_WRAPPED(name) name 36 | #define ERL_NIF_RETURN(value) return (value) 37 | 38 | 39 | #endif // ERL_NIF_DIRTY_SCHEDULER_SUPPORT 40 | 41 | 42 | #define ERL_NIF_WRAP_DIRTY_CPU(name) \ 43 | ERL_NIF_WRAP_DIRTY(name, ERL_NIF_DIRTY_JOB_CPU_BOUND) 44 | 45 | 46 | #define ERL_NIF_WRAP_DIRTY_IO(name) \ 47 | ERL_NIF_WRAP_DIRTY(name, ERL_NIF_DIRTY_JOB_IO_BOUND) 48 | 49 | 50 | #endif // Included nif_util.h 51 | -------------------------------------------------------------------------------- /apps/dirty_sched/rebar.config: -------------------------------------------------------------------------------- 1 | {port_specs, [ 2 | {"priv/dirty_sched.so", ["c_src/dirty_sched_using_macros.c"]} 3 | ]}. 4 | -------------------------------------------------------------------------------- /apps/dirty_sched/src/dirty_sched.app.src: -------------------------------------------------------------------------------- 1 | {application, dirty_sched, [ 2 | {description, "Examples using dirty schedulers"}, 3 | {vsn, "0.0.0"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {env, []} 7 | ]}. 8 | -------------------------------------------------------------------------------- /apps/dirty_sched/src/dirty_sched.erl: -------------------------------------------------------------------------------- 1 | -module(dirty_sched). 2 | -on_load(init/0). 3 | 4 | -export([ 5 | double/1, 6 | triple_defer/1, 7 | triple_direct/1 8 | ]). 9 | 10 | 11 | double(_N) -> 12 | not_loaded(?LINE). 13 | 14 | 15 | triple_defer(_N) -> 16 | not_loaded(?LINE). 17 | 18 | 19 | triple_direct(_N) -> 20 | not_loaded(?LINE). 21 | 22 | 23 | init() -> 24 | SoName = case code:priv_dir(?MODULE) of 25 | {error, bad_name} -> 26 | case filelib:is_dir(filename:join(["..", priv])) of 27 | true -> 28 | filename:join(["..", priv, ?MODULE]); 29 | _ -> 30 | filename:join([priv, ?MODULE]) 31 | end; 32 | Dir -> 33 | filename:join(Dir, ?MODULE) 34 | end, 35 | erlang:load_nif(SoName, 0). 36 | 37 | 38 | not_loaded(Line) -> 39 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 40 | -------------------------------------------------------------------------------- /apps/dirty_sched/test/dirty_sched_tests.erl: -------------------------------------------------------------------------------- 1 | -module(dirty_sched_tests). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | 6 | double_test() -> 7 | ?assertEqual(2, dirty_sched:double(1)). 8 | 9 | 10 | triple_defer_test() -> 11 | ?assertEqual(3, dirty_sched:triple_defer(1)). 12 | 13 | 14 | triple_direct_test() -> 15 | ?assertEqual(3, dirty_sched:triple_direct(1)). 16 | -------------------------------------------------------------------------------- /apps/resources/c_src/resources.c: -------------------------------------------------------------------------------- 1 | #include "erl_nif.h" 2 | 3 | ErlNifResourceType* RES_TYPE; 4 | ERL_NIF_TERM atom_ok; 5 | 6 | typedef struct 7 | { 8 | int count; 9 | } Tracker; 10 | 11 | typedef struct 12 | { 13 | int id; 14 | } Example; 15 | 16 | void 17 | free_res(ErlNifEnv* env, void* obj) 18 | { 19 | Tracker* tracker = (Tracker*) enif_priv_data(env); 20 | tracker->count -= 1; 21 | } 22 | 23 | static int 24 | open_resource(ErlNifEnv* env) 25 | { 26 | const char* mod = "resources"; 27 | const char* name = "Example"; 28 | int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; 29 | Tracker* tracker; 30 | 31 | RES_TYPE = enif_open_resource_type(env, mod, name, free_res, flags, NULL); 32 | if(RES_TYPE == NULL) return -1; 33 | return 0; 34 | } 35 | 36 | static int 37 | load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 38 | { 39 | if(open_resource(env) == -1) return -1; 40 | 41 | atom_ok = enif_make_atom(env, "ok"); 42 | 43 | tracker = (Tracker*) enif_alloc(sizeof(Tracker)); 44 | tracker->count = 0; 45 | *priv = (void*) tracker; 46 | 47 | return 0; 48 | } 49 | 50 | // Erlang requires that we re-open resources on re-initialisation. 51 | static int 52 | reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 53 | { 54 | if(open_resource(env) == -1) return -1; 55 | return 0; 56 | } 57 | 58 | static int 59 | reload(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info) 60 | { 61 | if(open_resource(env) == -1) return -1; 62 | return 0; 63 | } 64 | 65 | static ERL_NIF_TERM 66 | count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 67 | { 68 | Tracker* tracker; 69 | 70 | if(argc != 0) 71 | { 72 | return enif_make_badarg(env); 73 | } 74 | 75 | tracker = (Tracker*) enif_priv_data(env); 76 | return enif_make_int(env, tracker->count); 77 | } 78 | 79 | static ERL_NIF_TERM 80 | create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 81 | { 82 | Example* res; 83 | ERL_NIF_TERM ret; 84 | unsigned int id; 85 | Tracker* tracker; 86 | 87 | if(argc != 1) 88 | { 89 | return enif_make_badarg(env); 90 | } 91 | 92 | if(!enif_get_uint(env, argv[0], &id)) 93 | { 94 | return enif_make_badarg(env); 95 | } 96 | 97 | res = enif_alloc_resource(RES_TYPE, sizeof(Example)); 98 | if(res == NULL) return enif_make_badarg(env); 99 | 100 | ret = enif_make_resource(env, res); 101 | enif_release_resource(res); 102 | 103 | res->id = id; 104 | 105 | tracker = (Tracker*) enif_priv_data(env); 106 | tracker->count += 1; 107 | 108 | return enif_make_tuple2(env, atom_ok, ret); 109 | } 110 | 111 | static ERL_NIF_TERM 112 | read(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 113 | { 114 | Example* res; 115 | 116 | if(argc != 1) 117 | { 118 | return enif_make_badarg(env); 119 | } 120 | 121 | if(!enif_get_resource(env, argv[0], RES_TYPE, (void**) &res)) 122 | { 123 | return enif_make_badarg(env); 124 | } 125 | 126 | return enif_make_int(env, res->id); 127 | } 128 | 129 | static ErlNifFunc nif_funcs[] = { 130 | {"count", 0, count}, 131 | {"create", 1, create}, 132 | {"read", 1, read} 133 | }; 134 | 135 | ERL_NIF_INIT(resources, nif_funcs, &load, &reload, &upgrade, NULL); 136 | 137 | -------------------------------------------------------------------------------- /apps/resources/rebar.config: -------------------------------------------------------------------------------- 1 | {port_specs, [ 2 | {"priv/resources.so", ["c_src/*.c"]} 3 | ]}. -------------------------------------------------------------------------------- /apps/resources/src/resources.app.src: -------------------------------------------------------------------------------- 1 | {application, resources, [ 2 | {description, "Fun with resources"}, 3 | {vsn, "0.0.0"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {env, []} 7 | ]}. 8 | -------------------------------------------------------------------------------- /apps/resources/src/resources.erl: -------------------------------------------------------------------------------- 1 | -module(resources). 2 | -export([count/0, create/1, read/1]). 3 | -on_load(init/0). 4 | 5 | -define(APPNAME, resources). 6 | -define(LIBNAME, resources). 7 | 8 | count() -> 9 | not_loaded(?LINE). 10 | 11 | create(_) -> 12 | not_loaded(?LINE). 13 | 14 | read(_) -> 15 | not_loaded(?LINE). 16 | 17 | init() -> 18 | SoName = case code:priv_dir(?APPNAME) of 19 | {error, bad_name} -> 20 | case filelib:is_dir(filename:join(["..", priv])) of 21 | true -> 22 | filename:join(["..", priv, ?LIBNAME]); 23 | _ -> 24 | filename:join([priv, ?LIBNAME]) 25 | end; 26 | Dir -> 27 | filename:join(Dir, ?LIBNAME) 28 | end, 29 | erlang:load_nif(SoName, 0). 30 | 31 | not_loaded(Line) -> 32 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 33 | -------------------------------------------------------------------------------- /apps/resources/test/resource_tests.erl: -------------------------------------------------------------------------------- 1 | -module(resource_tests). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | basic_test() -> 6 | ?assertEqual(0, resources:count()), 7 | {ok, Res} = resources:create(90), 8 | ?assertEqual(1, resources:count()), 9 | ?assertEqual(90, resources:read(Res)), 10 | Fun = fun() -> 11 | {ok, Res2} = resources:create(18), 12 | ?assertEqual(2, resources:count()), 13 | ?assertEqual(18, resources:read(Res2)) 14 | end, 15 | Fun(), 16 | erlang:garbage_collect(), 17 | ?assertEqual(90, resources:read(Res)), 18 | ?assertEqual(1, resources:count()). 19 | -------------------------------------------------------------------------------- /apps/skeleton/c_src/skeleton.c: -------------------------------------------------------------------------------- 1 | #include "erl_nif.h" 2 | 3 | // There are four functions that may be called during the lifetime 4 | // of a NIF. load, reload, upgrade, and unload. Any of these functions 5 | // can be left unspecified by passing NULL to the ERL_NIF_INIT macro. 6 | // 7 | // NIFs are awesome. 8 | 9 | // Return value of 0 indicates success. 10 | // Docs: http://erlang.org/doc/man/erl_nif.html#load 11 | 12 | static int 13 | load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 14 | { 15 | return 0; 16 | } 17 | 18 | // Called when changing versions of the C code for a module's NIF 19 | // implementation if I read the docs correctly. 20 | // 21 | // Return value of 0 indicates success. 22 | // Docs: http://erlang.org/doc/man/erl_nif.html#upgrade 23 | 24 | static int 25 | upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info) 26 | { 27 | return 0; 28 | } 29 | 30 | // Called when the library is unloaded. Not called after a reload 31 | // executes. 32 | // 33 | // No return value 34 | // Docs: http://erlang.org/doc/man/erl_nif.html#load 35 | 36 | static void 37 | unload(ErlNifEnv* env, void* priv) 38 | { 39 | return; 40 | } 41 | 42 | // The actual C implementation of an Erlang function. 43 | // 44 | // Docs: http://erlang.org/doc/man/erl_nif.html#ErlNifFunc 45 | 46 | static ERL_NIF_TERM 47 | skeleton(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 48 | { 49 | return enif_make_badarg(env); 50 | } 51 | 52 | static ErlNifFunc nif_funcs[] = { 53 | {"skeleton", 1, skeleton} 54 | }; 55 | 56 | // Initialize this NIF library. 57 | // 58 | // Args: (MODULE, ErlNifFunc funcs[], load, reload, upgrade, unload) 59 | // Docs: http://erlang.org/doc/man/erl_nif.html#ERL_NIF_INIT 60 | 61 | ERL_NIF_INIT(skeleton, nif_funcs, &load, NULL, &upgrade, &unload); 62 | 63 | // Or if you don't need reload, upgrade, or unload. 64 | // ERL_NIF_INIT(skeleton, nif_funcs, &load, NULL, NULL, NULL); 65 | 66 | -------------------------------------------------------------------------------- /apps/skeleton/rebar.config: -------------------------------------------------------------------------------- 1 | {port_specs, [ 2 | {"priv/skeleton.so", ["c_src/*.c"]} 3 | ]}. 4 | -------------------------------------------------------------------------------- /apps/skeleton/src/skeleton.app.src: -------------------------------------------------------------------------------- 1 | {application, skeleton, [ 2 | {description, "NIF Skeleton structure"}, 3 | {vsn, "0.0.0"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {env, []} 7 | ]}. 8 | -------------------------------------------------------------------------------- /apps/skeleton/src/skeleton.erl: -------------------------------------------------------------------------------- 1 | -module(skeleton). 2 | -export([skeleton/1]). 3 | -on_load(init/0). 4 | 5 | % The name of the application we're writing. This is the name 6 | % used for the Erlang .app file. 7 | 8 | -define(APPNAME, skeleton). 9 | 10 | % The name of the shared library we're going to load the NIF 11 | % code from. Defined in rebar.config as so_name. 12 | 13 | -define(LIBNAME, skeleton). 14 | 15 | %% API 16 | 17 | % NIF functions end up overriding the functions defined in this module. But 18 | % this module must define the functions we want the NIF to implement. 19 | % Theoretically this won't ever get called as out on_load function init/0 20 | % should raise an error if we have issues. 21 | % 22 | % A really nice person would make a pure Erlang fallback incase a NIF was 23 | % unable to load for a specific platform. 24 | 25 | skeleton(_) -> 26 | not_loaded(?LINE). 27 | 28 | %% Iternal functions 29 | 30 | % Since we used init/0 in our -on_load() preprocessor directive, this 31 | % function will get called as the module is loaded. This is the perfect 32 | % place to load up our NIF shared library. Handily, the response of 33 | % erlang:load_nif/2 matches the return specification for -on_load() 34 | % functions. 35 | 36 | init() -> 37 | SoName = case code:priv_dir(?APPNAME) of 38 | {error, bad_name} -> 39 | case filelib:is_dir(filename:join(["..", priv])) of 40 | true -> 41 | filename:join(["..", priv, ?LIBNAME]); 42 | _ -> 43 | filename:join([priv, ?LIBNAME]) 44 | end; 45 | Dir -> 46 | filename:join(Dir, ?LIBNAME) 47 | end, 48 | erlang:load_nif(SoName, 0). 49 | 50 | % This is just a simple place holder. It mostly shouldn't ever be called 51 | % unless there was an unexpected error loading the NIF shared library. 52 | 53 | not_loaded(Line) -> 54 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 55 | -------------------------------------------------------------------------------- /apps/skeleton/test/skeleton_tests.erl: -------------------------------------------------------------------------------- 1 | -module(skeleton_tests). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | basic_test() -> 6 | ?assertException(error, badarg, skeleton:skeleton(foo)). 7 | -------------------------------------------------------------------------------- /apps/termsend/c_src/termsend.c: -------------------------------------------------------------------------------- 1 | #include "erl_nif.h" 2 | 3 | ERL_NIF_TERM 4 | mk_atom(ErlNifEnv* env, const char* atom) 5 | { 6 | ERL_NIF_TERM ret; 7 | 8 | if(!enif_make_existing_atom(env, atom, &ret, ERL_NIF_LATIN1)) 9 | { 10 | return enif_make_atom(env, atom); 11 | } 12 | 13 | return ret; 14 | } 15 | 16 | ERL_NIF_TERM 17 | mk_error(ErlNifEnv* env, const char* mesg) 18 | { 19 | return enif_make_tuple2(env, mk_atom(env, "error"), mk_atom(env, mesg)); 20 | } 21 | 22 | static ERL_NIF_TERM 23 | repeat(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 24 | { 25 | ErlNifEnv* msg_env; 26 | ErlNifPid pid; 27 | ERL_NIF_TERM copy; 28 | 29 | if(argc != 2) 30 | { 31 | return enif_make_badarg(env); 32 | } 33 | 34 | if(!enif_is_pid(env, argv[0])) 35 | { 36 | return mk_error(env, "not_a_pid"); 37 | } 38 | 39 | if(!enif_get_local_pid(env, argv[0], &pid)) 40 | { 41 | return mk_error(env, "not_a_local_pid"); 42 | } 43 | 44 | msg_env = enif_alloc_env(); 45 | if(msg_env == NULL) 46 | { 47 | return mk_error(env, "environ_alloc_error"); 48 | } 49 | 50 | copy = enif_make_copy(msg_env, argv[1]); 51 | 52 | if(!enif_send(env, &pid, msg_env, copy)) 53 | { 54 | enif_free(msg_env); 55 | return mk_error(env, "error_sending_term"); 56 | } 57 | 58 | enif_free_env(msg_env); 59 | return mk_atom(env, "ok"); 60 | } 61 | 62 | static ErlNifFunc nif_funcs[] = { 63 | {"repeat", 2, repeat} 64 | }; 65 | 66 | ERL_NIF_INIT(termsend, nif_funcs, NULL, NULL, NULL, NULL); 67 | 68 | -------------------------------------------------------------------------------- /apps/termsend/rebar.config: -------------------------------------------------------------------------------- 1 | {port_specs, [ 2 | {"priv/termsend.so", ["c_src/*.c"]} 3 | ]}. 4 | -------------------------------------------------------------------------------- /apps/termsend/src/termsend.app.src: -------------------------------------------------------------------------------- 1 | {application, termsend, [ 2 | {description, "Send terms from NIFs"}, 3 | {vsn, "0.0.0"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {env, []} 7 | ]}. 8 | -------------------------------------------------------------------------------- /apps/termsend/src/termsend.erl: -------------------------------------------------------------------------------- 1 | -module(termsend). 2 | -export([repeat/2]). 3 | -on_load(init/0). 4 | 5 | -define(APPNAME, termsend). 6 | -define(LIBNAME, termsend). 7 | 8 | repeat(_, _) -> 9 | not_loaded(?LINE). 10 | 11 | init() -> 12 | SoName = case code:priv_dir(?APPNAME) of 13 | {error, bad_name} -> 14 | case filelib:is_dir(filename:join(["..", priv])) of 15 | true -> 16 | filename:join(["..", priv, ?LIBNAME]); 17 | _ -> 18 | filename:join([priv, ?LIBNAME]) 19 | end; 20 | Dir -> 21 | filename:join(Dir, ?LIBNAME) 22 | end, 23 | erlang:load_nif(SoName, 0). 24 | 25 | % This is just a simple place holder. It mostly shouldn't ever be called 26 | % unless there was an unexpected error loading the NIF shared library. 27 | 28 | not_loaded(Line) -> 29 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 30 | -------------------------------------------------------------------------------- /apps/termsend/test/termsend_tests.erl: -------------------------------------------------------------------------------- 1 | -module(termsend_tests). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | next_message() -> 6 | receive 7 | Msg -> Msg 8 | after 1000 -> 9 | erlang:errot(timeout) 10 | end. 11 | 12 | basic_test() -> 13 | ?assertEqual(ok, termsend:repeat(self(), foo)), 14 | ?assertEqual(foo, next_message()). 15 | -------------------------------------------------------------------------------- /apps/termsend_threaded/c_src/termsend_threaded.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "erl_nif.h" 3 | 4 | typedef struct _qitem_t 5 | { 6 | struct _qitem_t* next; 7 | ErlNifPid* pid; 8 | } qitem_t; 9 | 10 | typedef struct 11 | { 12 | ErlNifMutex* lock; 13 | ErlNifCond* cond; 14 | qitem_t* head; 15 | qitem_t* tail; 16 | } queue_t; 17 | 18 | typedef struct 19 | { 20 | ErlNifThreadOpts* opts; 21 | ErlNifTid qthread; 22 | queue_t* queue; 23 | ERL_NIF_TERM atom_ok; 24 | } state_t; 25 | 26 | queue_t* 27 | queue_create() 28 | { 29 | queue_t* ret; 30 | 31 | ret = (queue_t*) enif_alloc(sizeof(queue_t)); 32 | if(ret == NULL) return NULL; 33 | 34 | ret->lock = NULL; 35 | ret->cond = NULL; 36 | ret->head = NULL; 37 | ret->tail = NULL; 38 | 39 | ret->lock = enif_mutex_create("queue_lock"); 40 | if(ret->lock == NULL) goto error; 41 | 42 | ret->cond = enif_cond_create("queue_cond"); 43 | if(ret->cond == NULL) goto error; 44 | 45 | return ret; 46 | 47 | error: 48 | if(ret->lock != NULL) enif_mutex_destroy(ret->lock); 49 | if(ret->cond != NULL) enif_cond_destroy(ret->cond); 50 | if(ret != NULL) enif_free(ret); 51 | return NULL; 52 | } 53 | 54 | void 55 | queue_destroy(queue_t* queue) 56 | { 57 | ErlNifMutex* lock; 58 | ErlNifCond* cond; 59 | 60 | enif_mutex_lock(queue->lock); 61 | assert(queue->head == NULL && "Destroying a non-empty queue."); 62 | assert(queue->tail == NULL && "Destroying queue in invalid state."); 63 | 64 | lock = queue->lock; 65 | cond = queue->cond; 66 | 67 | queue->lock = NULL; 68 | queue->cond = NULL; 69 | 70 | enif_mutex_unlock(lock); 71 | 72 | enif_cond_destroy(cond); 73 | enif_mutex_destroy(lock); 74 | enif_free(queue); 75 | } 76 | 77 | int 78 | queue_push(queue_t* queue, ErlNifPid* pid) 79 | { 80 | qitem_t* item = (qitem_t*) enif_alloc(sizeof(qitem_t)); 81 | if(item == NULL) return 0; 82 | 83 | item->pid = pid; 84 | item->next = NULL; 85 | 86 | enif_mutex_lock(queue->lock); 87 | 88 | if(queue->tail != NULL) 89 | { 90 | queue->tail->next = item; 91 | } 92 | 93 | queue->tail = item; 94 | 95 | if(queue->head == NULL) 96 | { 97 | queue->head = queue->tail; 98 | } 99 | 100 | enif_cond_signal(queue->cond); 101 | enif_mutex_unlock(queue->lock); 102 | 103 | return 1; 104 | } 105 | 106 | ErlNifPid* 107 | queue_pop(queue_t* queue) 108 | { 109 | qitem_t* item; 110 | ErlNifPid* ret = NULL; 111 | 112 | enif_mutex_lock(queue->lock); 113 | 114 | while(queue->head == NULL) 115 | { 116 | enif_cond_wait(queue->cond, queue->lock); 117 | } 118 | 119 | item = queue->head; 120 | queue->head = item->next; 121 | item->next = NULL; 122 | 123 | if(queue->head == NULL) 124 | { 125 | queue->tail = NULL; 126 | } 127 | 128 | enif_mutex_unlock(queue->lock); 129 | 130 | ret = item->pid; 131 | enif_free(item); 132 | 133 | return ret; 134 | } 135 | 136 | static void* 137 | thr_main(void* obj) 138 | { 139 | state_t* state = (state_t*) obj; 140 | ErlNifEnv* env = enif_alloc_env(); 141 | ErlNifPid* pid; 142 | ERL_NIF_TERM msg; 143 | 144 | while((pid = queue_pop(state->queue)) != NULL) 145 | { 146 | msg = enif_make_atom(env, "foo"); 147 | enif_send(NULL, pid, env, msg); 148 | enif_free(pid); 149 | enif_clear_env(env); 150 | } 151 | 152 | return NULL; 153 | } 154 | 155 | static int 156 | load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 157 | { 158 | state_t* state = (state_t*) enif_alloc(sizeof(state_t)); 159 | if(state == NULL) return -1; 160 | 161 | state->queue = queue_create(); 162 | if(state->queue == NULL) goto error; 163 | 164 | state->opts = enif_thread_opts_create("thread_opts"); 165 | if(enif_thread_create( 166 | "", &(state->qthread), thr_main, state, state->opts 167 | ) != 0) 168 | { 169 | goto error; 170 | } 171 | 172 | state->atom_ok = enif_make_atom(env, "ok"); 173 | 174 | *priv = (void*) state; 175 | 176 | return 0; 177 | 178 | error: 179 | if(state->queue != NULL) queue_destroy(state->queue); 180 | enif_free(state->queue); 181 | return -1; 182 | } 183 | 184 | static void 185 | unload(ErlNifEnv* env, void* priv) 186 | { 187 | state_t* state = (state_t*) priv; 188 | void* resp; 189 | 190 | queue_push(state->queue, NULL); 191 | enif_thread_join(state->qthread, &resp); 192 | queue_destroy(state->queue); 193 | 194 | enif_thread_opts_destroy(state->opts); 195 | enif_free(state); 196 | } 197 | 198 | static ERL_NIF_TERM 199 | send_to_pid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 200 | { 201 | state_t* state = (state_t*) enif_priv_data(env); 202 | ErlNifPid* pid = (ErlNifPid*) enif_alloc(sizeof(ErlNifPid)); 203 | 204 | if(!enif_get_local_pid(env, argv[0], pid)) 205 | { 206 | return enif_make_badarg(env); 207 | } 208 | 209 | queue_push(state->queue, pid); 210 | 211 | return state->atom_ok; 212 | } 213 | 214 | static ERL_NIF_TERM 215 | send_to_self(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 216 | { 217 | state_t* state = (state_t*) enif_priv_data(env); 218 | ErlNifPid* pid = (ErlNifPid*) enif_alloc(sizeof(ErlNifPid)); 219 | 220 | enif_self(env, pid); 221 | 222 | queue_push(state->queue, pid); 223 | 224 | return state->atom_ok; 225 | } 226 | 227 | static ErlNifFunc nif_funcs[] = { 228 | {"send_to_pid", 1, send_to_pid}, 229 | {"send_to_self", 0, send_to_self} 230 | }; 231 | 232 | ERL_NIF_INIT(termsend_threaded, nif_funcs, &load, NULL, NULL, &unload); 233 | 234 | -------------------------------------------------------------------------------- /apps/termsend_threaded/rebar.config: -------------------------------------------------------------------------------- 1 | {port_specs, [ 2 | {"priv/termsend_threaded.so", ["c_src/*.c"]} 3 | ]}. 4 | -------------------------------------------------------------------------------- /apps/termsend_threaded/src/termsend_threaded.app.src: -------------------------------------------------------------------------------- 1 | {application, termsend_threaded, [ 2 | {description, "NIF that sends terms from a thread."}, 3 | {vsn, "0.0.0"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {env, []} 7 | ]}. 8 | -------------------------------------------------------------------------------- /apps/termsend_threaded/src/termsend_threaded.erl: -------------------------------------------------------------------------------- 1 | -module(termsend_threaded). 2 | -export([send_to_pid/1, send_to_self/0]). 3 | -on_load(init/0). 4 | 5 | -define(APPNAME, termsend_threaded). 6 | -define(LIBNAME, termsend_threaded). 7 | 8 | send_to_pid(_) -> 9 | not_loaded(?LINE). 10 | 11 | send_to_self() -> 12 | not_loaded(?LINE). 13 | 14 | init() -> 15 | SoName = case code:priv_dir(?APPNAME) of 16 | {error, bad_name} -> 17 | case filelib:is_dir(filename:join(["..", priv])) of 18 | true -> 19 | filename:join(["..", priv, ?LIBNAME]); 20 | _ -> 21 | filename:join([priv, ?LIBNAME]) 22 | end; 23 | Dir -> 24 | filename:join(Dir, ?LIBNAME) 25 | end, 26 | erlang:load_nif(SoName, 0). 27 | 28 | not_loaded(Line) -> 29 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 30 | -------------------------------------------------------------------------------- /apps/termsend_threaded/test/termsend_threaded_tests.erl: -------------------------------------------------------------------------------- 1 | -module(termsend_threaded_tests). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | 6 | repeat_messages(Dst) -> 7 | receive 8 | Msg -> 9 | Dst ! {repeated, Msg} 10 | end, 11 | repeat_messages(Dst). 12 | 13 | 14 | next_message() -> 15 | receive 16 | Msg -> Msg 17 | after 1000 -> 18 | erlang:errot(timeout) 19 | end. 20 | 21 | 22 | send_to_self_test() -> 23 | ?assertEqual(ok, termsend_threaded:send_to_self()), 24 | ?assertEqual(foo, next_message()). 25 | 26 | 27 | send_to_pid_test() -> 28 | Self = self(), 29 | Pid = spawn_link(fun() -> repeat_messages(Self) end), 30 | ?assertEqual(ok, termsend_threaded:send_to_pid(Pid)), 31 | ?assertEqual({repeated, foo}, next_message()). 32 | 33 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davisp/nif-examples/af140533e6bd0e10e8d409bf9b0c57e798a55c38/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {sub_dirs, [ 2 | "apps/dirty_sched", 3 | "apps/resources", 4 | "apps/skeleton", 5 | "apps/termsend", 6 | "apps/termsend_threaded" 7 | ]}. 8 | -------------------------------------------------------------------------------- /shell: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ERL_LIBS="./apps/:$ERL_LIBS" exec erl 4 | --------------------------------------------------------------------------------