Skip to content
Browse files

initial commit, supports gcproc:spawn/1, gcproc:send/2, gcproc:pid/2

  • Loading branch information...
0 parents commit 32c6dd9c5b023062ffa6c57fc78d41d4058eb1d0 @gleber committed Nov 20, 2012
Showing with 267 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +27 −0 Makefile
  3. +45 −0 README.md
  4. +5 −0 rebar.config
  5. +12 −0 src/gcproc.app.src
  6. +33 −0 src/gcproc.erl
  7. +142 −0 src/gcproc_manager.erl
3 .gitignore
@@ -0,0 +1,3 @@
+/.eunit/
+/deps/
+/ebin/
27 Makefile
@@ -0,0 +1,27 @@
+REBAR=$(shell which rebar || echo ./rebar)
+
+all: $(REBAR)
+ $(REBAR) get-deps compile
+
+tests: $(REBAR)
+ $(REBAR) eunit skip_deps=true suite=dstree
+
+sh: all
+ erl -smp enable -pa ebin/ -pa deps/*/ebin/ -pa .eunit/ -eval 'shell_default:m(gcproc)'
+
+test: tests
+
+clean:
+ $(REBAR) clean skip_deps=true
+
+# Detect or download rebar
+
+REBAR_URL=http://cloud.github.com/downloads/basho/rebar/rebar
+./rebar:
+ erl -noshell -s inets -s ssl \
+ -eval 'httpc:request(get, {"$(REBAR_URL)", []}, [], [{stream, "./rebar"}])' \
+ -s init stop
+ chmod +x ./rebar
+
+distclean:
+ rm -f ./rebar
45 README.md
@@ -0,0 +1,45 @@
+# GCproc #
+
+This library implements garbage-collected processes by (ab)using
+garbage collection of NIF resources.
+
+Please note that node will be garbage collected along with the
+resource, hence it may take some short time before it's terminated due
+to way BEAM GC works.
+
+Note: work only with SMP enabled!
+
+## Usage ##
+
+Example:
+```erlang
+1> results(0). %% make sure shell doesn't cache results
+20
+2> ok = application:start(gcproc).
+ok
+3> G = gcproc:spawn(fun() -> receive ok -> ok end end).
+{gcproc,<0.40.0>,{resource,143010032,<<>>}}
+4> is_process_alive(pid(0,40,0)).
+true %% process is still running
+5> f(). %% forget all shell bindings
+ok
+6> is_process_alive(pid(0,40,0)).
+false %% process is not running anymore!
+
+```
+
+Sending message
+```erlang
+9> G = gcproc:spawn(fun() -> receive done -> io:format("Receive works!") end, receive ok -> ok end end).
+{gcproc,<0.49.0>,{resource,143008592,<<>>}}
+10> G:send(done). %% equivalent to "G:pid() ! done"
+Receive works!
+done
+11> is_process_alive(pid(0,49,0)).
+true
+12> f().
+ok
+13> is_process_alive(pid(0,49,0)).
+false
+
+```
5 rebar.config
@@ -0,0 +1,5 @@
+%% -*- mode: erlang -*-
+
+{deps, [
+ {resource, ".*", {git, "https://github.com/tonyrog/resource.git", "HEAD"}}
+ ]}.
12 src/gcproc.app.src
@@ -0,0 +1,12 @@
+{application, gcproc, [
+ {description, "gcproc - garbage collectable processes"},
+ {vsn, "0.1.0"},
+ {modules, []},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]},
+ {mod, {gcproc_manager, []}},
+ {env, []}
+]}.
33 src/gcproc.erl
@@ -0,0 +1,33 @@
+-module(gcproc).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-export([spawn/1,
+
+ send/2, pid/1]).
+
+-record(gcproc, {pid, res}).
+
+spawn(Fun) ->
+ Pid = erlang:spawn(Fun),
+ Res = resource:notify_when_destroyed(whereis(gcproc_manager), {timeout, Pid}),
+ #gcproc{pid = Pid, res = Res}.
+
+send(Msg, #gcproc{pid = Pid}) ->
+ Pid ! Msg.
+
+pid(#gcproc{pid = Pid}) ->
+ Pid.
+
+
+simple_test() ->
+ ok = application:start(gcproc),
+ Self = self(),
+ spawn_link(fun() ->
+ G = gcproc:spawn(fun() -> receive ok -> ok end end),
+ true = erlang:is_process_alive(G:pid()),
+ Self ! {pid, G:pid()}
+ end),
+ Pid = receive {pid, P} -> P end,
+ timer:sleep(100),
+ ?assertEqual(false, erlang:is_process_alive(Pid)).
142 src/gcproc_manager.erl
@@ -0,0 +1,142 @@
+%%%-------------------------------------------------------------------
+%%% @author <gleber@first.lan>
+%%% @copyright (C) 2012,
+%%% @doc
+%%%
+%%% @end
+%%% Created : 20 Nov 2012 by <gleber@first.lan>
+%%%-------------------------------------------------------------------
+-module(gcproc_manager).
+
+-behaviour(gen_server).
+
+%% application API
+-export([start/2]).
+
+%% gen_server API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
+%% @end
+%%--------------------------------------------------------------------
+start(_, _) ->
+ start_link().
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+init([]) ->
+ {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @spec handle_call(Request, From, State) ->
+%% {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_info({timeout, Pid}, State) ->
+ case erlang:is_process_alive(Pid) of
+ true ->
+ catch exit(Pid, shutdown);
+ false ->
+ ok
+ end,
+ {noreply, State};
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================

0 comments on commit 32c6dd9

Please sign in to comment.
Something went wrong with that request. Please try again.