Bug with ProcessInfo.processInfo.environment on linux

Hi, I am trying to use ProcessInfo.processInfo.environment on Linux but I ran into SR-6968.

Basically, it caches the result the first time environment is accessed - subsequent calls always return the same and don’t reflect any changes made to environment variables in the meantime (which Darwin does).

I debugged it down into CoreFoundation and I see that the logic in __CFGetEnvironment() is wrapped in dispatch_once() which explains why the environment variables are only read once.

I want to ask why dispatch_once() was chosen and should it be replaced by dispatch_sync() instead? Or is there another approach that should be taken to fix it?

This found while using Swift 4.0.3 and Ubuntu 16.04.

Code to recreate bug:

import Foundation

let preset = ProcessInfo.processInfo.environment["test"]
setenv("test", "worked", 1)
let postset = ProcessInfo.processInfo.environment["test"]
print(preset)
print(postset)

I think __CFGetEnvrionment should stay the same, but we should probably implement the ProcessInfo.environment call not to use it. The CF call is really for CF’s internal use only. As you noted, on Darwin, Foundation doesn’t call into it for this method.

That sounds good. How would you suggest we change ProcessInfo.environment? Would we use getenv instead?

You can’t use getenv since that is for single key lookups, but you can walk the environment and convert it to a Dictionary:

open var environment: [String : String] {
        let equalSign = Int32(UInt8(ascii: "="))
        var env: [String : String] = [:]
        var idx = 0

        while let entry = environ.advanced(by: idx).pointee {
            if let value = strchr(entry, equalSign) {
                let keyLen = entry.distance(to: value)
                guard keyLen > 0 else {
                    continue
                }
                if let key = NSString(bytes: entry, length: keyLen, encoding: String.Encoding.utf8.rawValue) {
                    env[key._swiftObject] = String(cString: value.advanced(by: 1))
                }
            } else {
                let key = String(cString: entry)
                env[key] = ""
            }
            idx += 1
        }
        return env
    }

This may need modifiying depending on how empty key names are handled in the environment etc.

That fix looks good and i see you have already created a pull request. Thank you Tony and Simon for your help in solving this issue.