KEEP icon indicating copy to clipboard operation
KEEP copied to clipboard

Common Atomics and Atomic Arrays

Open mvicsokolova opened this issue 1 year ago • 19 comments

This issue is for discussion of the proposal to introduce Common Atomic and Atomic Array types in the standard library. The full text of the proposal is here: proposals/common-atomics.md.

The KEEP presents the proposed API for Common Atomics and Atomic Arrays and focuses on the implementation options for the JVM backend.

PR: #397

mvicsokolova avatar Nov 07 '24 11:11 mvicsokolova

I want to expand a little more on "why not value class". You mention that the objects will have no identity, but I don't see the issue with that. Couldn't we just have the value class's equals method check equality of underlying Java atomic reference? I think that equality for atomics only makes sense as reference equality. In other words, I'd be very surprised if AtomicInt(0) == AtomicInt(0) I'm also unclear as to what this means:

Loosely defined different value semantics on different platforms

and for the point:

Value classes are not interoperable with Java, e.g., signatures of functions with inline classes get mangled

There's 2 solutions. One can use @JvmName to stop the mangling (which makes sense in an interop situation. #393 might be a potential solution as well.

kyay10 avatar Nov 07 '24 13:11 kyay10

I think the atomics should also come with an assign method by default that works with the assign plugin

kyay10 avatar Nov 07 '24 13:11 kyay10

Streamlining the kotlinx-atomicfu machinery to Kotlin is an explicit next step of this proposal and a subject of the next KEEP after atomics stabilization.

Does this mean that atomicfu would become part of the Kotlin compiler and stdlib (with one of the solutions mentioned) rather than an external plugin?

In a similar vein, are there longer term plans for multiplatform Kotlin concurrent collections (i.e. ConcurrentHashMap, maybe even ConcurrentSkipListMap like classes)?

rnett avatar Nov 07 '24 15:11 rnett

@kyay10 thank you for your comment!

On implementing JVM atomics as value classes:

  • Referential equality operator (===) is not defined for value classes by design, comparing AtomicIntInline(0) === AtomicIntInline(0) would lead to a compilation error. While for other platforms (Native/Js/Wasm) atomics can be compared by reference.

  • On function mangling:

@JvmInline
value class AtomicIntInline(val a: AtomicInteger)

fun foo(x: AtomicIntInline) {...} // calling this function from Java results in an resolution error, since it's name is mangled by the compiler

Marking foo with a @JvmName annotation would allow calling foo from Java, though a user would have to add this annotation manually, which does not look like a good solution.

@JvmExposeBoxed described in #393 could become a solution for this issue, it would expose a boxed version of foo: public foo(other: AtomicIntInline)

mvicsokolova avatar Nov 07 '24 18:11 mvicsokolova

My point about referential equality is that it's unnecessary since normal equality will suffice (it'll compare the underlying references in case of JVM). In other words, I'd expect that AtomicIntInline(0) == AtomicIntInline(0) is false, and hence that I can use regular equality on all platforms (so that Native and JS aren't "losing out" on reference comparisons, they just have to use == in place of ===)

WRT function mangling, maybe we want a variant of #393 that exposes the underlying type instead of a boxed type, so that one can basically say "my value class is morally the same as its underlying type, I'm just trying to have a different API than its underlying type, but Java users don't need to know about any of that". Exposing the boxed types is fine IMO too, Java users will just have to deal with converting it.

kyay10 avatar Nov 07 '24 19:11 kyay10

@rnett yes, the plan is to turn kotlinx-atomicfu into a compiler plugin, which would provide optimization by inlining stdlib atomics marked with some special @InlineAtomic annotation or e.g. created using a factory function atomic(value: Int) (the final solution has not yet been designed). At that stage the library will be removed.

mvicsokolova avatar Nov 08 '24 10:11 mvicsokolova

Some more thoughts on implementing atomics as value classes on JVM:

  • On JVM, a value class wrapping a Java atomic would introduce an extra layer of boxing when e.g. a list of atomics is created.
  • If we implement atomics as value classes on JVM, atomics should be value classes on all other backends as well (currently, expected and actual declarations do not allow this distinction, and changing this would require some hacks in the compiler). And for K/N value classes would not work, because an atomic could only be passed to a function as a copy of the wrapped value, not the reference, which would allow to update the value atomically.

mvicsokolova avatar Dec 02 '24 11:12 mvicsokolova

Re. JS, the KEEP says "implementation will be single threaded", which is fine, but will the atomic usages be inlined?
For example, wrapping a number into an AtomicInt class would mean an unnecessary level of indirection.

lppedd avatar Dec 05 '24 17:12 lppedd

Introduce a special annotation (e.g. @InlineAtomic) to mark the atomics, which a user wants to inline. Then the atomicfu compiler plugin checks constraints for those atomics and inlines them.

And from https://github.com/Kotlin/kotlinx-atomicfu/issues/493

Users will have control over which atomics to inline by applying a dedicated annotation, such as @InlineAtomic

Wouldn't it be better from a DX standpoint to always try to inline, but allow a consumer to opt-out in case the compiler reports a constraint violation? E.g. with a @NoInline annotation.

lppedd avatar Dec 06 '24 09:12 lppedd

Wouldn't it be better from a DX standpoint to always try to inline, but allow a consumer to opt-out in case the compiler reports a constraint violation? E.g. with a @NoInline annotation.

@lppedd that's actually a good point for discussion, thank you! A little bit later I'll create an issue in kotlinx-atomicfu repo with the description of this new optimization compiler plugin in more details: the scope of features, support of JS, and with the options of the user interface (@InlineAtomic or @NoInline annotation) etc.

mvicsokolova avatar Dec 06 '24 10:12 mvicsokolova

Re. JS, the KEEP says "implementation will be single threaded", which is fine, but will the atomic usages be inlined?

Yes, inlining of kotlin.concurrent atomics for JS will be considered in the new light atomicfu compiler plugin.

mvicsokolova avatar Dec 06 '24 10:12 mvicsokolova

The documentation (and implementation) for compareAndSet and compareAndExchange of AtomicReference and compareAndSetAt and compareAndExchangeAt of AtomicArray seems to be inconsistent across platforms.

The documentation for Common and Native says this:

Comparison of values is done by reference.

But the documentation for JVM, JS and Wasm says this:

Comparison of values is done by value.

For JVM this seems to be wrong, the API specification mentions == (the reference equality operator in Java).

For JS and Wasm, these methods are indeed implemented using ==/!= (the value equality operators in Kotlin, which calls equals). Is this intended?

lukellmann avatar Dec 09 '24 19:12 lukellmann

Small nitpick about the naming scheme (if it is not too late).

It seems that the names of AtomicReference<T> and AtomicArray<T> are inconsistent. In Java, AtomicReference<T> and AtomicReferenceArray<T> follow a consistent pattern, as do other AtomicX and AtomicXArray pairs.

By analogy, Kotlin then should use either:

  • AtomicReference<T> and AtomicReferenceArray<T>, or
  • Atomic<T> and AtomicArray<T>.

eupp avatar Feb 26 '25 23:02 eupp

While using the same naming scheme for scalar and array reference types looks nice, there are a few reasons to keep the current scheme (AtomicReference and AtomicArray) untouched:

  • renaming AtomicReference<T> to just Atomic<T> may result in misunderstanding of semantics for unexperienced users, who might expect that all operations on something wrapped into Atomic are now atomic as well. You can see that misunderstanding among, for example, some C++ developers, who expect that placing an object into std::atomic will make it atomic itself. AtomicReference explicitly states what is atomic: the reference to an object, not the object itself, so the semantic is clear.
  • with the AtomicArray, the existing name reflects the semantic: it's an array that could be updated atomically. And the name is also consistent with what you can load from that array: AtomicIntArray -> Int; AtomicLongArray -> Long, AtomicArray<T> -> T (and not Reference<T> or AtomicReference<T>).

So while aligning AtomicArray and AtomicReference names could improve aesthetic properties, we would rather keep the names asymmetric to reflect semantics better.

fzhinkin avatar May 28 '25 14:05 fzhinkin

Opened PR that adds information about update-functions that will be introduced in Kotlin 2.2.20: https://github.com/Kotlin/KEEP/pull/432

fzhinkin avatar Jun 13 '25 19:06 fzhinkin

Just wondering what's the status of the transition to the stdlib and Kotlin compiler from AtomicFU.
There are multiple places where I could have asked, so pardonne moi if it's not the ideal one.

lppedd avatar Sep 03 '25 17:09 lppedd

@lppedd, the transition is planned, but nobody is working on it at the moment. Do you expect a significant performance drop after switching to common atomic types (w/o compiler support) in your projects?

fzhinkin avatar Sep 03 '25 18:09 fzhinkin

@fzhinkin thanks!

Do you expect a significant performance drop after switching to common atomic types (w/o compiler support) in your projects?

I don't think so. But my question was more geared towards understanding what to expect in the coming months, so I can update our timeline for devops/refactoring activities. Obviously the less dependencies the better, but if the experience isn't as good as it is with AtomicFU, it doesn't make sense getting rid of it. I will just postone as needed.

lppedd avatar Sep 04 '25 20:09 lppedd

@lppedd

but if the experience isn't as good as it is with AtomicFU

I think, it is difficult to beat AtomicFU in providing a "good" experience. ;)

Atomic types provided by the stdlib are not "inlined" the same way AFU atomics are inlined by the plugin (well, they are just boxed types), but otherwise they are functionally equivalent. Stdlib types are still experimental, but they are less experimental than the AtomicFU library in general, in some sense.

fzhinkin avatar Sep 05 '25 18:09 fzhinkin