Bug-fix release addressing memory-safety and silent-wrong-answer defects when a shared vector is accessed after its segment has been closed.
close() or
segment_close(). Shared vectors cache the resolved base pointer across
reads, and closing a segment unmapped it without invalidating that cache, so
a read performed before the close left a pointer into unmapped memory that a
later read would reuse. Depending on whether the operating system had reused
the address range, this crashed the R session or silently returned garbage
values. Segment teardown now invalidates all cached pointers. Views were
affected through their parent segment as well."underlying shared memory segment is no longer valid" from every access
path. Previously only the DATAPTR paths reported the error: element access
returned NA, and the region-copy methods used by sum(), mean() and
max() returned a short count without filling the caller's buffer, so those
reductions consumed uninitialized memory and returned arbitrary values.
Coercion and materialization returned zero-filled vectors. length()
continues to work on a closed vector, as it reads no shared memory.segment_protect() is called on a segment that
already has a writable shared vector over it. Each vector captured the
segment's read-only state at creation, so it kept handing out writable
pointers into a mapping that had since been made read-only, and the write
faulted. Protection now replaces the shared mapping with a single
process-private copy-on-write mapping. Read-only operations that ask for a
writable data pointer -- including range(), which.max() and comparison
operators -- therefore keep their zero-copy behavior, while a real classed
mutation is explicitly materialized and writes through an unclass()
bypass cannot reach disk or another process. Vectors created by share()
and as_shared() were unaffected, as those protect the segment before
building the vector. The same stale flag was written into the serialized
form of such a vector, so sending one to a worker re-opened the segment
read-write and writes reached the shared bytes; serialization now carries
the effective state. shared_diagnostics() likewise reports the effective
read-only state rather than the value captured at creation.pool_health_check() no longer aborts with "missing value where TRUE/FALSE needed" when a worker's resident-set size cannot be read. This
happened when a worker exited between the liveness probe and the memory
reading, or when a baseline captured at spawn was itself unavailable; such
workers are now left for the next tick's liveness check instead, and the
health report says so rather than reporting all workers healthy.
pool_create() now rejects an NA rss_limit or rss_drift_threshold,
which reached the same comparison.segment_write() now reports the write size, offset and segment size when a
write does not fit, instead of a bare "Write failed". Writes to an
unmapped segment are rejected with a distinct message.Internal performance and correctness refactor. No user-facing API was removed;
apart from a new path= argument to as_shared() and the deprecation of the
unused pool_create(heartbeat_interval=), all changes improve the behavior,
speed, or robustness of the existing runtime.
diagnostics = TRUE. This sharply reduces
dispatch wall-clock and task-payload size on many-shard workloads.backing = "auto" now maps shared memory on tmpfs; added
madvise() access hints and a cached segment base pointer.buf[], as.vector(buf), per-shard slice reads)
now use a single typed C read instead of a raw intermediate plus readBin(),
halving allocation and copy volume when materializing results.ps handle cached at spawn time (also more
robust against PID reuse) instead of creating one per check per poll tick.stream_map() no longer grow result lists
incrementally across phases/partitions.seed= now installs an independent per-shard L'Ecuyer-CMRG RNG stream
immediately before each shard runs, so results are identical regardless of
workers, chunk_size, or shard-to-worker assignment, and are stable across
worker recycling and restarts.cow = "deny" is enforced for shared inputs at the R level; deep-environment
validation for shared objects is preserved.XLENGTH), Windows read-only
segment protection, and copy/coercion accounting.view_col_vars() and the col_vars kernel use a numerically stable (Welford)
computation.shard_map() validates borrow/out names against the worker function's
formals before creating the pool, failing fast with a clearer message.shard_reduce(): minimal reducer environment, per-shard seed streams, and
corrected automatic chunking and init semantics.share() for atomic vectors over 2GB
(about 269 million doubles).shard_reduce() receiving a stale output-buffer handle when run after
shard_map() on the same pool with a same-named out= buffer.table_write() validates shard_id, so an NA id can no longer produce a
part file that table_finalize() silently drops.row_layout() erroring when a trailing shard has zero rows, and
shard_crossprod() failing on matrices with fewer than 8 columns.ps package."shm" backing now works: generated segment names previously
exceeded the Darwin name-length limit, so shm_open() always failed.shard_map(health_check_interval=) is honoured when supplied explicitly
(profile presets no longer override it), and share(name=) now applies to
fast-path atomic shares. pool_create(heartbeat_interval=) is deprecated:
it was never consulted (health checks are per-chunk, not time-based).autotune_block_size() now clamps correctly to max_shards_per_worker and
warns when a scratch budget would require more shards than allowed.options(shard.workers = N)
or the SHARD_WORKERS environment variable (still capped to 2 under
R CMD check).tasks[1]
trailing-array idiom; changed to a C99 flexible array member._R_CHECK_LIMIT_CORES_ and are capped at
2 during R CMD check, at all call sites (pool_create(), shard_map(),
shards(), shard_reduce()).shard_map() and shard_reduce() for parallel map and reduce operations.