Open-Source Wikis

/

Ruby

/

Systems

/

Require, autoload, and the loader

ruby/ruby

Require, autoload, and the loader

load.c (~55 KB) implements Kernel#require, Kernel#load, Kernel#autoload, and the $LOAD_PATH ($:) machinery. The loader manages the cache of already-loaded files, enforces thread/Ractor safety during loading, and bridges to C extensions via dln.c.

Purpose

  • Implement require, require_relative, load, and autoload.
  • Maintain $LOADED_FEATURES ($") so each feature loads at most once.
  • Resolve a feature name to a file path, looking through $LOAD_PATH.
  • Load Ruby source via the parser/compiler/VM, and load C extensions via dlopen.
  • Be safe across threads, Ractors, and Fiber Schedulers.

Files

File Purpose
load.c Core require/load/autoload.
loadpath.c $LOAD_PATH setup and standard library path injection.
dln.c Cross-platform dynamic library loader (dln_load, dln_find_exe).
dln_find.c Path searching helper.
dln.h Public interface.
kernel.rb Ruby-level Kernel#require_relative etc.
lib/bundled_gems.rb The list of bundled-gem requires that emit a warning when activated implicitly.

$LOAD_PATH ordering

$LOAD_PATH is the array of directories searched on require "name". At startup it's populated by loadpath.c::ruby_init_loadpath:

1. directories from -I command-line flags
2. directories from RUBYLIB env var
3. site_ruby (system-wide additions): <prefix>/lib/ruby/site_ruby/<api-ver>
4. vendor_ruby: <prefix>/lib/ruby/vendor_ruby/<api-ver>
5. gem-installed default-gem paths
6. stdlib: <prefix>/lib/ruby/<api-ver>

Order matters: the first matching file wins. Conflicting names between user code, gems, and stdlib are resolved this way.

How require works

graph LR
    call["require 'foo'"] --> resolve[Resolve via $LOAD_PATH]
    resolve --> seen{seen in $LOADED_FEATURES?}
    seen -->|yes| ret[return false]
    seen -->|no| lock[Acquire load lock for path]
    lock --> kind{Ruby or .so?}
    kind -->|.rb| parse[Parse + compile + execute]
    kind -->|.so| dlopen[dlopen + Init_xxx]
    parse --> add[Add path to $LOADED_FEATURES]
    dlopen --> add
    add --> ret2[return true]

load.c::rb_require_internal is the entry point. Highlights:

  • Both forms (name and name.rb/name.so) are accepted; the loader tries .rb first then .so.
  • The "feature name" stored in $LOADED_FEATURES is the resolved absolute path. So require 'foo/bar' and require './foo/bar.rb' (with ./foo/bar.rb matching) deduplicate correctly.
  • A per-feature lock prevents two threads from racing to load the same file. The second thread blocks until the first finishes; if the first thread is in the current Ractor, the lock is non-recursive.

Autoload

autoload(:Foo, "foo") registers a placeholder for Foo: the first reference to the constant triggers require "foo". Implementation in variable.c::autoload_state and load.c::rb_autoload_load.

autoload :HTTP, 'net/http'
HTTP   # at this point, 'net/http' is required and HTTP must be defined

Autoload is thread-safe: concurrent references on different threads block until the loading thread finishes.

autoload?(:Foo) returns the registered file (or nil).

Kernel#autoload_relative (added in Ruby 4.x — see NEWS.md) resolves the path relative to the calling file, mirroring require_relative.

Bundled-gem warnings

When Ruby 3.4+ is asked to require 'csv' (or another gem that has graduated from default to bundled status), lib/bundled_gems.rb emits a one-time warning suggesting the user add gem 'csv' to their Gemfile. The mechanism is a hook installed at boot time.

C extension loading

dln_load("foo.so") (in dln.c) opens the shared library and calls its Init_foo function. The Init_xxx symbol is the convention every CRuby C extension follows:

void
Init_mygem(void)
{
    VALUE m = rb_define_module("MyGem");
    rb_define_singleton_method(m, "hello", mygem_hello, 0);
}

dln.c handles platform differences:

  • POSIX: dlopen + dlsym.
  • Windows: LoadLibrary + GetProcAddress.
  • Static linking: a registry of Init_* functions in dmydln.c and dmyext.c.

The static-linking path is used by builds like the WASM port (wasm/) where dynamic loading isn't available.

Compaction and the loader

The loader registers per-feature data with the GC. When the GC compacts, paths and feature names move; load.c::loaded_features_index_clear_i rebuilds the path-to-index hash after compaction.

$LOADED_FEATURES vs $LOAD_PATH

  • $LOAD_PATH ($:) — directories searched.
  • $LOADED_FEATURES ($") — files already loaded (full paths or short names).
  • Both are arrays; standard array methods work, but mutating them during a require is undefined.

Ractor interaction

require runs only in the main Ractor by default. Calling it from another Ractor raises Ractor::IsolationError. The reason is that $LOAD_PATH and $LOADED_FEATURES are global state, plus loading code defines methods on shared classes (which is also forbidden from non-main Ractors).

Ractor.make_shareable doesn't help here — the restriction is structural.

Common pitfalls

  • Modifying $LOAD_PATH after gems load: gems are activated based on $LOAD_PATH order at boot. Late additions don't re-resolve old activations.
  • Cyclic requires: A requires B requires A. Ruby allows it, but the partially-loaded version is returned. Constants defined later may not exist yet — leading to NameError.
  • Symlinks across require_relative: require_relative resolves the directory of the current file, which on symlinked layouts can surprise.
  • require 'rubygems' is implicit: removed long ago; you don't need it.

Entry points for modification

  • Loader bug: load.c::rb_require_internal and the helpers search_required / loaded_features_index_add.
  • New autoload variant: extend variable.c::autoload_state_* and surface from kernel.rb.
  • Path search order: loadpath.c::ruby_init_loadpath_safe builds the initial array.
  • C extension loading on a new platform: extend dln.c with the platform's dynamic-loader API.

See extensions.md for the C extension story and reference/configuration.md for $LOAD_PATH env vars.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Require, autoload, and the loader – Ruby wiki | Factory