[R-pkg-devel] Check if any object pointing to a given address is locked

2025-04-24 Thread Tony Wilkes
Hi everyone,

I have a question.

Given the address of an object ("address" in the sense given by, for example, 
"data.table::address()"), is it possible to check if any binding in any 
environment pointing to that address is locked?
So a function similar to "bindingIsLocked()", except the function checks all 
bindings sharing the same address, instead of checking a binding by name in a 
specific environment.

One use-case for this is to check if it's save to modify an object by reference.
For example, when saying x <- base::letters, x is not save to modify by 
reference:
Although the binding of x itself is not locked, the binding of base::letters is 
locked, and we obviously don't want to modify base::letters.

It should be possible, I think. R has a few pass-by-reference classes (like 
environments), so R should be able to check something like this. I just have no 
idea how exactly R checks this.

Thanks in advance for any response!

Kind regards,

Tony



[[alternative HTML version deleted]]

__
R-package-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-package-devel


Re: [R-pkg-devel] Check if any object pointing to a given address is locked

2025-04-24 Thread Ivan Krylov via R-package-devel
В Thu, 24 Apr 2025 12:36:57 +
Tony Wilkes  пишет:

> One use-case for this is to check if it's save to modify an object by
> reference.

I think you might need NOT_SHARED() for that, except it's far from
being simple, especially once you pass arbitrary expressions (not just
variable names) as arguments to your function:
https://developer.r-project.org/Refcnt.html

> For example, when saying x <- base::letters, x is not save
> to modify by reference: Although the binding of x itself is not
> locked, the binding of base::letters is locked, and we obviously
> don't want to modify base::letters.

The situation is more dangerous than that. When 'x' references the same
object as base::letters (which is a locked binding), you can still
modify this object from C code, given its SEXP address, and see the
changes from either binding; R won't be able to stop you. (That's the
danger of by-reference operations like those implemented in data.table.)

On the other hand, if you assign a new value to the 'x' binding inside
a given environment, base::letters and any other objects referencing it
will retain their original values; there is no need to worry about
other bindings being locked.

> R has a few pass-by-reference classes (like environments)

These are different. You can lock a _binding_ to an environment and
still change the _value_ of that environment precisely because it's a
mutable, pass-by-reference object:

x <- new.env()
y <- x
lockBinding('x', environment())
# x$foo <- 'bar' # would signal an error, but:
assign('foo', 'bar', envir = x) # works
x$foo # [1] "bar"
identical(x, y) # [1] TRUE

R switched to reference counting by default in version 4.0. One
downside of the reference counts as currently implemented is that when
an object containing references to other objects is garbage-collected,
the reference counts of the child objects are not decremented:

x <- 1
invisible(replicate(999, list(x)))
gc(full = TRUE)
.Internal(inspect(x))
# @5618cd515488 14 REALSXP g1c1 [MARK,REF(2000)] (len=1, tl=0) 1

'x' retains a reference count of 2000 despite the 999 lists that
previously referenced it no longer exist. This is possible to change,
but at the cost of slowing down the garbage collector.

-- 
Best regards,
Ivan

__
R-package-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-package-devel