Browse Source

First implementation of environment variable handling.

The idea is to let gproc act as a cache for environment variables, and
at the same time provide a streamlined way of checking alternative sources
of configuration. Caching the values in gproc makes it easier to see what
each process assumes about its environment, and the option to inherit values
from a parent should make it easier to manage configuration.

First implementation. Tests still need to be written.
Ulf Wiger 14 years ago
parent
commit
c306076df2
2 changed files with 313 additions and 14 deletions
  1. 114 3
      doc/gproc.md
  2. 199 11
      src/gproc.erl

+ 114 - 3
doc/gproc.md

@@ -146,7 +146,7 @@ a = aggregate_counter
 
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Read the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#nb_wait-1">nb_wait/1</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registeration entry given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#get_env-3">get_env/3</a></td><td>Equivalent to <a href="#get_env-4"><tt>get_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_env-4">get_env/4</a></td><td>Fetch an environment value, potentially cached as a <code>gproc_env</code> property.</td></tr><tr><td valign="top"><a href="#get_set_env-3">get_set_env/3</a></td><td>Equivalent to <a href="#get_set_env-4"><tt>get_set_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_set_env-4">get_set_env/4</a></td><td>Fetch and cache an environment value, if not already cached.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Read the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#nb_wait-1">nb_wait/1</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_env-5">set_env/5</a></td><td>Updates the cached value as well as underlying environment.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registeration entry given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr></table>
 
 
 
@@ -338,7 +338,98 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 Behaves as ets:first(Tab) for a given type of registration object.
 
 See [`http://www.erlang.org/doc/man/ets.html#first-1`](http://www.erlang.org/doc/man/ets.html#first-1).
-The registry behaves as an ordered_set table.<a name="get_value-1"></a>
+The registry behaves as an ordered_set table.<a name="get_env-3"></a>
+
+<h3>get_env/3</h3>
+
+
+
+
+
+<pre>get_env(Scope::<a href="#type-scope">scope()</a>, App::atom(), Key::atom()) -> term()</pre>
+<br></br>
+
+
+
+
+Equivalent to [`get_env(Scope, App, Key, [app_env])`](#get_env-4).<a name="get_env-4"></a>
+
+<h3>get_env/4</h3>
+
+
+
+
+
+<pre>get_env(Scope::<a href="#type-scope">scope()</a>, App::atom(), Key::atom(), Strategy) -> term()</pre>
+<ul class="definitions"><li><pre>Strategy = [Alternative]</pre></li><li><pre>Alternative = app_env | os_env | inherit | {inherit, pid()} | {default, term()} | error</pre></li></ul>
+
+
+
+
+
+Fetch an environment value, potentially cached as a `gproc_env` property.
+
+
+
+This function first tries to read the value of a cached property,
+`{p, Scope, {gproc_env, App, Key}}`. If this fails, it will try the provided
+alternative strategy. `Strategy` is a list of alternatives, tried in order.  
+Each alternative can be one of:
+
+
+
+* `app_env` - try `application:get_env(App, Key)`
+* `os_env` - try `os:getenv(ENV)`, where `ENV` is `Key` converted into an
+uppercase string
+* `{os_env, ENV}` - try `os:getenv(ENV)`
+* `inherit` - inherit the cached value, if any, held by the (proc_lib) parent.
+* `{inherit, Pid}` - inherit the cached value, if any, held by `Pid`.
+* `{default, Value}` - set a default value to return once alternatives have been
+exhausted; if not set, `undefined` will be returned.
+* `error` - raise an exception, `erlang:error(gproc_env, [App, Key, Scope])`.
+
+
+
+While any alternative can occur more than once, the only one that might make
+sense to repeat is `{default, Value}`. The last instance will be the one that  
+determines the return value.
+
+The `error` option can be used to assert that a value has been previously
+cached. Alternatively, it can be used to assert that a value is either cached
+or at least defined somewhere, e.g. `get_env(l, mnesia, dir, [app_env, error])`.<a name="get_set_env-3"></a>
+
+<h3>get_set_env/3</h3>
+
+
+
+
+
+<pre>get_set_env(Scope::<a href="#type-scope">scope()</a>, App::atom(), Key::atom()) -> term()</pre>
+<br></br>
+
+
+
+
+Equivalent to [`get_set_env(Scope, App, Key, [app_env])`](#get_set_env-4).<a name="get_set_env-4"></a>
+
+<h3>get_set_env/4</h3>
+
+
+
+
+
+<pre>get_set_env(Scope::<a href="#type-scope">scope()</a>, App::atom(), Key::atom(), Strategy) -> Value</pre>
+<br></br>
+
+
+
+
+Fetch and cache an environment value, if not already cached.
+
+
+__See also:__ [This function does the same thing as [`get_env/4`](#get_env-4), but also updates the
+cache. Note that the cache will be updated even if the result of the lookup
+is `undefined`.](#get_env-4).<a name="get_value-1"></a>
 
 <h3>get_value/1</h3>
 
@@ -830,7 +921,27 @@ Sends a message to the process, or processes, corresponding to Key.
 If Key belongs to a unique object (name or aggregated counter), this
 function will send a message to the corresponding process, or fail if there
 is no such process. If Key is for a non-unique object type (counter or
-property), Msg will be send to all processes that have such an object.<a name="set_value-2"></a>
+property), Msg will be send to all processes that have such an object.<a name="set_env-5"></a>
+
+<h3>set_env/5</h3>
+
+
+
+
+
+<pre>set_env(Scope::<a href="#type-scope">scope()</a>, App::atom(), Key::atom(), Value::term(), Strategy) -> Value</pre>
+<ul class="definitions"><li><pre>Strategy = [Alternative]</pre></li><li><pre>Alternative = app_env | os_env | {os_env, VAR}</pre></li></ul>
+
+
+
+
+
+Updates the cached value as well as underlying environment.
+
+This function should be exercised with caution, as it affects the larger
+environment outside gproc. This function modifies the cached value, and then
+proceeds to update the underlying environment (OS environment variable or
+application environment variable).<a name="set_value-2"></a>
 
 <h3>set_value/2</h3>
 

+ 199 - 11
src/gproc.erl

@@ -68,6 +68,11 @@
          last/1,
          table/1, table/2]).
 
+%% Environment handling
+-export([get_env/3, get_env/4,
+	 get_set_env/3, get_set_env/4,
+	 set_env/5]).
+
 %% Convenience functions
 -export([add_local_name/1,
          add_global_name/1,
@@ -269,7 +274,201 @@ lookup_local_counters(P)    -> lookup_values({c,l,P}).
 %%
 lookup_global_counters(P)   -> lookup_values({c,g,P}).
 
+%% @spec get_env(Scope::scope(), App::atom(), Key::atom()) -> term()
+%% @equiv get_env(Scope, App, Key, [app_env])
+get_env(Scope, App, Key) ->
+    get_env(Scope, App, Key, [app_env]).
+
+%% @spec (Scope::scope(), App::atom(), Key::atom(), Strategy) -> term()
+%%   Strategy = [Alternative]
+%%   Alternative = app_env
+%%               | os_env
+%%               | inherit | {inherit, pid()}
+%%               | {default, term()}
+%%               | error
+%% @doc Fetch an environment value, potentially cached as a `gproc_env' property.
+%%
+%% This function first tries to read the value of a cached property,
+%% `{p, Scope, {gproc_env, App, Key}}'. If this fails, it will try the provided
+%% alternative strategy. `Strategy' is a list of alternatives, tried in order.
+%% Each alternative can be one of:
+%% 
+%% * `app_env' - try `application:get_env(App, Key)'
+%% * `os_env' - try `os:getenv(ENV)', where `ENV' is `Key' converted into an 
+%%   uppercase string
+%% * `{os_env, ENV}' - try `os:getenv(ENV)'
+%% * `inherit' - inherit the cached value, if any, held by the (proc_lib) parent.
+%% * `{inherit, Pid}' - inherit the cached value, if any, held by `Pid'.
+%% * `{default, Value}' - set a default value to return once alternatives have been
+%%    exhausted; if not set, `undefined' will be returned.
+%% * `error' - raise an exception, `erlang:error(gproc_env, [App, Key, Scope])'.
+%%
+%% While any alternative can occur more than once, the only one that might make 
+%% sense to repeat is `{default, Value}'. The last instance will be the one that 
+%% determines the return value.
+%%
+%% The `error' option can be used to assert that a value has been previously 
+%% cached. Alternatively, it can be used to assert that a value is either cached
+%% or at least defined somewhere, e.g. `get_env(l, mnesia, dir, [app_env, error])'.
+%% @end
+get_env(Scope, App, Key, Strategy)
+  when Scope==l, is_atom(App), is_atom(Key);
+       Scope==g, is_atom(App), is_atom(Key) ->
+    do_get_env(Scope, App, Key, Strategy, false).
+
+%% @spec get_set_env(Scope::scope(), App::atom(), Key::atom()) -> term()
+%% @equiv get_set_env(Scope, App, Key, [app_env])
+get_set_env(Scope, App, Key) ->
+    get_set_env(Scope, App, Key, [app_env]).
+
+%% @spec get_set_env(Scope::scope(), App::atom(), Key::atom(), Strategy) -> Value
+%% @doc Fetch and cache an environment value, if not already cached.
+%%
+%% @see get_env/4.
+%% This function does the same thing as {@link get_env/4}, but also updates the 
+%% cache. Note that the cache will be updated even if the result of the lookup
+%% is `undefined'.
+%% @end
+%%
+get_set_env(Scope, App, Key, Strategy)
+  when Scope==l, is_atom(App), is_atom(Key);
+       Scope==g, is_atom(App), is_atom(Key) ->
+    do_get_env(Scope, App, Key, Strategy, true).
+
+do_get_env(Context, App, Key, Alternatives, Set) ->
+    case lookup_env(Context, App, Key, self()) of
+	undefined ->
+	    check_alternatives(Alternatives, Context, App, Key, undefined, Set);
+	{ok, Value} ->
+	    Value
+    end.
+
+%% @spec set_env(Scope::scope(), App::atom(),
+%%               Key::atom(), Value::term(), Strategy) -> Value
+%%   Strategy = [Alternative]
+%%   Alternative = app_env | os_env | {os_env, VAR}
+%%
+%% @doc Updates the cached value as well as underlying environment.
+%%
+%% This function should be exercised with caution, as it affects the larger
+%% environment outside gproc. This function modifies the cached value, and then
+%% proceeds to update the underlying environment (OS environment variable or 
+%% application environment variable).
+%% @end
+%%
+set_env(Scope, App, Key, Value, Strategy)
+  when Scope==l, is_atom(App), is_atom(Key);
+       Scope==g, is_atom(App), is_atom(Key) ->
+    case is_valid_set_strategy(Strategy, Value) of
+	true ->
+	    cache_env(Scope, App, Key, Value),
+	    set_strategy(Strategy, App, Key, Value);
+	false ->
+	    erlang:error(badarg)
+    end.
+
+check_alternatives([{default, Val}|Alts], Scope, App, Key, _, Set) ->
+    check_alternatives(Alts, Scope, App, Key, Val, Set);
+check_alternatives([H|T], Scope, App, Key, Def, Set) ->
+    case try_alternative(H, App, Key, Scope) of
+	undefined ->
+	    check_alternatives(T, Scope, App, Key, Def, Set);
+	{ok, Value} ->
+	    if Set ->
+		    cache_env(Scope, App, Key, Value),
+		    Value;
+	       true ->
+		    Value
+	    end
+    end;
+check_alternatives([], Scope, App, Key, Def, Set) ->
+    if Set ->
+	    cache_env(Scope, App, Key, Def);
+       true ->
+	    ok
+    end,
+    Def.
+
+try_alternative(error, App, Key, Scope) ->
+    erlang:error(gproc_env, [App, Key, Scope]);
+try_alternative(inherit, App, Key, Scope) ->
+    case get('$ancestors') of
+	[P|_] ->
+	    lookup_env(Scope, App, Key, P);
+	_ ->
+	    undefined
+    end;
+try_alternative({inherit, P}, App, Key, Scope) ->
+    lookup_env(Scope, App, Key, P);
+try_alternative(app_env, App, Key, _Scope) ->
+    case application:get_env(App, Key) of
+	undefined       -> undefined;
+	{ok, undefined} -> undefined;
+	{ok, Value}     -> {ok, Value}
+    end;
+try_alternative(os_env, _App, Key, _) ->
+    case os:getenv(os_env_key(Key)) of
+	""  -> undefined;
+	Val -> {ok, Val}
+    end;
+try_alternative({os_env, Key}, _, _, _) ->
+    case os:getenv(Key) of
+	""  -> undefined;
+	Val -> {ok, Val}
+    end;
+try_alternative({mnesia,Type,Key,Pos}, _, _, _) ->
+    case mnesia:activity(Type, fun() -> mnesia:read(Key) end) of
+	[] -> undefined;
+	[Found] ->
+	    {ok, element(Pos, Found)}
+    end.
+
+os_env_key(Key) ->
+    string:to_upper(atom_to_list(Key)).
+
+lookup_env(Scope, App, Key, P) ->
+    case ets:lookup(?TAB, {{p, Scope, {gproc_env, App, Key}}, P}) of
+	[] ->
+	    undefined;
+	[{_, _, Value}] ->
+	    {ok, Value}
+    end.
 
+cache_env(Scope, App, Key, Value) ->
+    reg({p, Scope, {gproc_env, App, Key}}, Value).
+
+is_valid_set_strategy([os_env|T], Value) ->
+    is_string(Value) andalso is_valid_set_strategy(T, Value);
+is_valid_set_strategy([{os_env, _}|T], Value) ->
+    is_string(Value) andalso is_valid_set_strategy(T, Value);
+is_valid_set_strategy([app_env|T], Value) ->
+    is_valid_set_strategy(T, Value);
+is_valid_set_strategy([], _) ->
+    true;
+is_valid_set_strategy(_, _) ->
+    false.
+
+set_strategy([H|T], App, Key, Value) ->
+    case H of
+	app_env ->
+	    application:set_env(App, Key, Value);
+	os_env ->
+	    os:putenv(os_env_key(Key), Value);
+	{os_env, ENV} ->
+	    os:putenv(ENV, Value)
+    end,
+    set_strategy(T, App, Key, Value);
+set_strategy([], _, _, Value) ->
+    Value.
+
+is_string(S) ->
+    try begin _ = iolist_to_binary(S),
+	      true
+	end
+    catch
+	error:_ ->
+	    false
+    end.
 
 %% @spec reg(Key::key()) -> true
 %%
@@ -472,9 +671,6 @@ local_mreg(T, [_|_] = KVL) ->
         {true,_}  -> monitor_me()
     end.
 
-
-
-
 %% @spec (Key :: key(), Value) -> true
 %% @doc Sets the value of the registeration entry given by Key
 %% 
@@ -507,9 +703,6 @@ set_value({c,l,_} = Key, Value) when is_integer(Value) ->
 set_value(_, _) ->
     erlang:error(badarg).
 
-
-
-
 %% @spec (Key) -> Value
 %% @doc Read the value stored with a key registered to the current process.
 %%
@@ -605,9 +798,6 @@ my_is_process_alive(_) ->
     %% remote pid - assume true (too costly to find out)
     true.
 
-
-
-
 %% @spec (Key::key()) -> [{pid(), Value}]
 %%
 %% @doc Retrieve the `{Pid,Value}' pairs corresponding to Key.
@@ -625,8 +815,6 @@ lookup_values({T,_,_} = Key) ->
         end,
     [Pair || {P,_} = Pair <- L, my_is_process_alive(P)].
 
-
-
 %% @spec (Key::key(), Incr::integer()) -> integer()
 %%
 %% @doc Updates the counter registered as Key for the current process.