Issue #14009 has been updated by myst (Boaz Segev).


I should point out that `"Foundation.framework/Foundation"` seems to be enough.

This is the C code I'm currently considering for the iodine Ruby server.

~~~ c
#ifdef __APPLE__
  void *obj_c_runtime = dlopen("Foundation.framework/Foundation", RTLD_LAZY);
  /* iodine runs here and `fork` might be called */
  dlclose(obj_c_runtime);
#else
  /* iodine runs here and `fork` might be called */
#endif
~~~

Comments:

* The `__APPLE__` directive will only work on `gcc` (gnu compiler), `clang` and intel compilers (other compilers might be supported, but I don't know).

* The code doesn't check for errors, because a quite failure doesn't effect behavior. If Objective-C isn't available, than forking will not be effected.

* The code doesn't check for High Sierra specifically and will load Objective-C on all macOS versions (anything over 10.9).

The memory increase was from 4388Kb (Ruby VM) to ~4560Kb (compared using Fiddle, not a direct C call), which seems insignificant on the larger scale of things.

For comparison:

* Baseline: loading the Objective-C using Fiddle caused a 3.9% increase in memory consumption relative to the basic the VM core.

* Loading the Socket Standard library (`require 'socket'`) caused a 16% increase in memory consumption relative to the basic the VM core.

* Loading Sinatra caused more than a 196% increase in memory consumption relative to the basic the VM core.

* Loading Rails caused more than a 264% increase in memory consumption relative to the basic the VM core.

It's my understanding that a simple patch will load the Objective-C library within the `ruby_init()` function and ignore any errors in loading the library.

A more complex approach will lazy load the library only when `Process#fork` is called... but this might effect C extensions that might call `fork` directly and it might also effect the use of Ruby as a library (when `ruby_init()` is called within a C application that uses Ruby internally). 

Personally, I'm both lazy and paranoid and I would assume that the simple approach would both work better and fail better (expose issues earlier rather than later).

----------------------------------------
Bug #14009: macOS High Sierra and °»fork°… compatibility
https://bugs.ruby-lang.org/issues/14009#change-67205

* Author: ticky (Jessica Stokes)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin17]
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN
----------------------------------------
This was originally discussed on the issue tracker for Puma (https://github.com/puma/puma/issues/1421), however, it is possible that it would make more sense for inclusion in the Ruby implementation itself.

macOS High Sierra has changed the behaviour of the fork syscall such that initialising Objective-C APIs in forked processes are treated as errors. (see http://sealiesoftware.com/blog/archive/2017/6/5/Objective-C_and_fork_in_macOS_1013.html for more details)

This means that many applications which use forking to process concurrently will forcibly crash if the forked process calls out to any Objective-C library when Objective-C was not already initialised in the host process. This includes Puma, Unicorn, iodine and Passenger.

A workaround I proposed for Puma was to implicitly load the Objective-C runtime before performing any forks (https://github.com/puma/puma/issues/1421#issuecomment-332650703). This causes forked processes using other Objective-C APIs to not crash.

The workaround (specific to Puma°«s DSL) was:

~~~ ruby
# Work around macOS 10.13 and later being very picky about
# `fork` usage and interactions with Objective-C code
# see: <https://github.com/puma/puma/issues/1421>
if /darwin/ =~ RUBY_PLATFORM
  before_fork do
    require 'fiddle'
    # Dynamically load Foundation.framework, ~implicitly~ initialising
    # the Objective-C runtime before any forking happens in Puma
    Fiddle.dlopen '/System/Library/Frameworks/Foundation.framework/Foundation'
  end
end
~~~

A similar fix has now been included in Passenger (https://github.com/phusion/passenger/blob/2a55a84e5de721d8bd806a8fea0bcedf27583c29/src/ruby_supportlib/phusion_passenger/loader_shared_helpers.rb#L84-L105).

It was, however, proposed that it might make more sense for Ruby on macOS High Sierra and onward to implicitly initialise the Objective-C framework itself, so that forked processes work roughly as expected even if they intend to use Objective-C APIs.

I understand that this is a heavy-handed move, but it seems to me that this relatively common technique will remain broken in Ruby unless everyone deploys a workaround (iodine has already expressed disinterest in doing so) or Ruby adopts one at the higher level.

This issue is also applicable to all Ruby versions which support fork and run on macOS High Sierra.

Thank you for your time. :)



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>