Skip to content

Commit 14cdf76

Browse files
committed
Add Safety section to ENV documentation
1 parent e2f43c5 commit 14cdf76

File tree

1 file changed

+33
-0
lines changed

1 file changed

+33
-0
lines changed

src/env.cr

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,31 @@ require "crystal/system/env"
1313
#
1414
# NOTE: All keys and values are strings. You must take care to cast other types
1515
# at runtime, e.g. integer port numbers.
16+
#
17+
# ### Safety
18+
#
19+
# Modifying the environment in single-threaded programs is safe. Modifying the
20+
# environment is also always safe on Windows.
21+
#
22+
# Modifying the environment in multi-threaded programs on other targets is
23+
# always unsafe, and can cause a mere read to segfault! At best, memory will be
24+
# leaked every time the environment is modified.
25+
#
26+
# The problem is that POSIX systems don't guarantee a thread safe implementation
27+
# of the `getenv`, `setenv` and `putenv` libc functions. Any thread that gets an
28+
# environment variable while another thread sets an environment variable may
29+
# segfault. The `ENV` object itself is internally protected by a readers-writer
30+
# lock, but we can't protect against external libraries, including libc calls
31+
# made by the stdlib. They might call `getenv` internally without holding the
32+
# read lock while a crystal fiber with the write lock calls `setenv`.
33+
#
34+
# The only safe solution is to consider `ENV` to be immutable, and to never call
35+
# `ENV.[]=`, `ENV.delete` or `ENV.clear` in your program. If you really need to,
36+
# you must make sure that no other thread has been started (beware of libraries
37+
# that may start threads).
38+
#
39+
# NOTE: Passing environment variables to a child process should use the `env`
40+
# arg of `Process.run` and `Process.new`.
1641
module ENV
1742
extend Enumerable({String, String})
1843

@@ -34,6 +59,9 @@ module ENV
3459
# If *value* is `nil`, the environment variable is deleted.
3560
#
3661
# If *key* or *value* contains a null-byte an `ArgumentError` is raised.
62+
#
63+
# WARNING: It is recommended to never set environment variables. See the
64+
# Safety section of `ENV` for details.
3765
def self.[]=(key : String, value : String?)
3866
Crystal::System::Env.set(key, value)
3967

@@ -90,6 +118,9 @@ module ENV
90118

91119
# Removes the environment variable named *key*. Returns the previous value if
92120
# the environment variable existed, otherwise returns `nil`.
121+
#
122+
# WARNING: It is recommended to never delete environment variables. See the
123+
# Safety section of `ENV` for details.
93124
def self.delete(key : String) : String?
94125
if value = self[key]?
95126
Crystal::System::Env.set(key, nil)
@@ -113,6 +144,8 @@ module ENV
113144
end
114145
end
115146

147+
# WARNING: It is recommended to never delete environment variables. See the
148+
# Safety section of `ENV` for details.
116149
def self.clear : Nil
117150
keys.each { |k| delete k }
118151
end

0 commit comments

Comments
 (0)