Synchronized to modifier?


#1

Can we convert @Synchronized into a modifier, please?

Rationale:

  • Locking is not optional. Code that uses the synchronized modifier should not compile correctly in KotlinJS as Javascript has no concept of shared memory multi-threading. But with an annotation based approach, it would.
  • There are no parameters to this annotation, so it can easily be a modifier.
  • When using JVM reflection APIs, this will show up as a synchronized modifier instead of annotation, thus yielding a confusing and pointless disparity between source and what Java reflection sees
  • @Synchronized looks uglier than in Java  :)

#2

At first I was totally agreeing with you, but on second thought I'm having doubts, for the follwing reasons:

  • synchronized on a method is actually bad because the monitor is leaked by the class instance itself. So I think the case can be made that we shouldn't have such annotation or modifier at all.
  • a synchronized method can accomplish the same functionality, thus something like "fun <T:Any> T.synchronized(monitor:Any = this, body: ()->Unit)"
  • library functions shouldn't be neglected.
So I'm not saying I'm definitily favor one approach over another, just putting some extra arguments on the table.

#3

The problem with defining certain common code patterns as bad is you lose the ability to automatically convert Java to Kotlin, which is a key and unique selling point. If a Java class has methods marked as synchronized, then they need to be converted exactly, as otherwise Java code that reflects on the classes will spot the difference even if the code has been rewritten to lock itself in other ways.


#4

Yes being able to convert from Java to Kotlin is very important and you still can with what I'm proposing.

Java:

public synchronized void foo() {   //... }

Kotlin:

public fun foo() {   synchronized(this) {   //...   } }

In other words, you can still do bad things. But in this case atleast it is exposed that the monitor is the class instance itself.

Update: by using a function you can, if you want, also create an empty inline function effectively compiling the whole synchronized function away. Just as an alternative on the JS platform.


#5

First of all it's widely considered to be an anti-pattern to use synchronized methods in Java. Then, we don't want to tailor Kotlin to any particular cocurrency model.


#6

The two forms you give are identical. Nothing is gained by the second form except verbosity, so I am not sure why it's meant to be superior.

Synchronized methods are not appropriate for all cases, but it’s surprising to me that they’re suddenly considered objectionable. They are very widely used.


#7

There are 4.4 million uses of the word synchronized in Java source on github - I am surprised to hear that it's now considered somehow deprecated or not worthy of first class support. It's a very common thing to encounter in Java, even though it's not perfect.

With respect to tailoring, that argument would make sense to me if there was no @Synchronized annotation at all (though lacking it would be a problem), but as it does exist, my only argument is about the right form for it to take, rather than the desirability of it. It seems the arguments for it being a modifier rather than an annotation are reasonable, or at least, I did not yet see any rebuttal.


#8

Well the difference is that the Java example is actually a modifier and thus part of the langange and the Kotlin example can be just a regular function and thus not part of the language. Furthermore the latter is still necessary to be compatible with Java as the sychronized keyword can also contain the monitor. So 'superior' isn't a wording I would choose, 'desirable' covers it better.

And yes the synchronized keyword is widely used in methods, even on the Java standard library, but that doesn’t tell us much whether it’s a good idea or not. Hence, checked exceptions and nulls are also part of Java and also widely used. But they are inconvenient most of the time.


#9

@Synchronized is also a part of the language. The compiler recognises it, and it affects the runtime behaviour of the code. The rationale is the same as for converting @inline to a modifier, etc.


#10

I meant the grammer. And yes I follow your rational. Furthermore, if the decission is made that this annotation is here to stay I agree with you that a modifier probably makes more sense.

But honestly, I didn’t even know this annotation was there. And that is simply because it is indeed widely considered to be a bad practise (in Java) so I for one wasn’t looking for it. It’s the principle of instance confinement that brought us the monitor pattern which is the safer alternative. So that made think that it might be a good idea to ditch the annotation (and thus the idea of modifier) altogether and only provide the synchronized function that is still necessary in the first place. All the existing Java code can still be converted.


#11

Were you measuring the uses of the word "synchronized" (both as a modifier and a synchronized block) or just synchronized methods?

> With respect to tailoring, that argument would make sense to me if there was no @Synchronized annotation at all

The annotation is available on the JVM only, as well as @Strictfp etc. So, it’s a part of the platform, not of the language


#12

Well, searching for "public synchronized" yields 1.1 million.

I wonder if it really makes sense to try and keep the Kotlin language itself entirely backend independent, or whether it’s better to have two related/mostly identical languages that share a subset. From a users perspective, if they’re targeting the JVM and don’t plan to target JS as well, the platform independence just results in confusing quirks and stuff that’s less direct than in Java. I’m guessing “targeting the JVM and not JS” will represent a big chunk of Kotlin’s userbase, especially given the decision to deprioritise JS support for version 1.

Making @Synchronized an annotation means the same code can technically parse with the KotlinJS compiler, but in reality that’s useless because it can’t be translated properly. Though KotlinJS can just ignore this annotation and would by default, it can also be programmed to ignore modifiers just as easily.

So having @Synchronized be an annotation ends up being (perhaps) more convenient or theoretically pure for the Kotlin compiler authors. But it results in a confusing semantic mismatch on the JVM where something that looks like an annotation will be seen by reflection and frameworks as a modifier … as well as being a minor difference from Java that increases the learning curve.


#13

> I wonder if it really makes sense to try and keep the Kotlin language itself entirely backend independent I believe is does.

> But it results in a confusing semantic mismatch on the JVM where something that looks like an annotation will be seen by reflection and frameworks as a modifier … as well as being a minor difference from Java that increases the learning curve.

I do not believe that this semantic mismatch is important. Java-only code sees it as in Java, that’s all.

BTW, Scala doesn’t seem to have synchronized methods at all, and it seems to work fine there.


#14

Just my two cents here but I believe an annotation is a metadata that can either be retrieved by reflection or used by a processor to support / add non standard feature. Also, the compiler can use annotations as metadata to check things (like @Overrides in java). However, using an annotation to add a feature supported in the compiler's standard and that changes the JVM bytecode output seems to me in contradiction to the very purpose of annotations and counter-intuitive. Whether Kotlin should support synchronized methods is another debate and it would be a valid decision for Kotlin to remove the support. However, should Kotlin support this feature in its standard, I believe it should definitely be a keyword.


#15

> However, using an annotation to add a feature supported in the compiler's standard and that changes the JVM bytecode output seems to me in contradiction to the very purpose of annotations and counter-intuitive.

Well, I think you are using an overly restrictive definition of “purpose of annotations”. Kotlin features a few things other than @Synchronized that would have to be built into the language by your logic:

  • @JvmStatic - sets the ACC_STATIC flag (and alters quite a few things in the bytecode too)
  • @JvmName - sets the name of a Java method
    • @file:JvmName - sets the name of a class that contains top-level functions from the given file
    • @file:JvmMultifileClass - allows to put top-level functions from many files to one class (multifile class)
  • @JvmOverloads - generates Java overloads from a Kotlin function with default parameter values
  • @Volatile, @Strictfp, @Transient (and the infamous @Synchronized) - alter corresponding ACC_* flags
  • @Throws - writes an exception signature to the .class file

My opinion is that adding all these to the language syntax whould be a little bit over the top :)

Note one important common trait in these annotations: they are all handled primarily by the JVM-specific back-end of the compiler, the front-end only cares about checking their applicability (actually, it's a JVM-specific part of the front-end). I.e. these annotations do not really affect the language at all, they are directives to the code generator.

Now, let’s look at Kotlin’s modifiers:

  • abstract, open, final, sealed, override: drive inheritance, obviously affect the language
  • enum: alters the syntax of the declaration (allows enum entry list)
  • annotation: alters the syntax: annotation classes have no bodies
  • inner: alters scoping: members of outer class are accessible
  • private, public, internal, protected: regulate visibility, affect name/overload resolution
  • out, in: affect subtyping
  • vararg: affects use-site syntax, types and overload resolution
  • companion: affects both use-site and declaration-site syntax
  • lateinit: affects the syntax: initializer is forbidden, no definite-assignment checks are performed
  • inline, noinline, crossinline: affect possible usages of local returns etc
  • reified: affects possible arguments and uses of a type parameter inside
  • tailrec: affects allowed contents of a function
  • external: affects syntax: non-abstract function with no body
  • data: affects the set of members of a class, inheritance rules etc
So, all these matter a lot to the front-end of the compiler: some even to the parser, others to the type checker and name resolution algorithms. This, I believe, draws a pretty clear divide between what's a modifier built into the language, and what's merely a platform-specific annotation.

#16

Your explanation makes a lot of sense ... from the perspective of a compiler writer.

As someone who just uses the language though, the distribution of code between backend and frontend is unknowable. In this light the distinction between “public” and @Transient appears quite arbitary. They are both modifier-like-things, but are spelled differently.

You say that @Throws, @Strictfp, @JvmStatic etc would have to be modifiers rather than annotations in order to be consistent with this world view and I’d say … well, yes! Just like in Java! I’m not sure why it’d be over the top - is there some sort of modifier budget that isn’t apparent?

Annotations always made sense to me as a way for third parties to extend the language, but for language designers themselves to use such things is still an idea I’m getting used to (and yeah, I’m not a big fan of @Override in Java either … I’m not sure why it also couldn’t just be a keyword).

I realise that at the start of Kotlin there was a strong desire to unify modifiers and annotations. It’d definitely have been cool to make the language work in this way. But as modifiers turned out to have some technical advantages and that unification has not happened, we are left with this odd state where Kotlin has the compiler architecture poking through into the language design and a distribution of modifiers-vs-keywords that looks, to the uninitiated outsider, pretty much random.

I’m wondering if the resistance to converting more annotations into modifiers is a holdover from the initial dream of metadata unification?


#17

> Your explanation makes a lot of sense ... from the perspective of a compiler writer.

It makes a lot of sense from the perspective of a language designer. Modifiers are heavy, only necessary things must become modifiers, annotations are lightweight, inessential things can be made annotations (that's in part why Java has @Override, although ading modifiers is just too expensive in Java, and they went even further with @PolymorphicSignature, which, to my taste, maybe a little edgy, but a very pragmatic choice in fact).

I think that the distinction helps the users to understand the divide between the language itself and the platform, and it is good. @Synchoized is something like System.exit(), essentially a library thing, not something built into the language, and it’s good to know that it is from the first glance.


#18

You're right, so let me see if I can reword : - @JvmStatic, @JvmName, @JvmOverloads, @Strictfp, @Throws : Changes the generated JVM bytecode but not the semantic of the kotlin code nor the semantic of the generated code (it changes names and access method, but not semantic). - @Transient : Kind of a dilema to me. It applies to virtually all serialization framework, not just Java. Whether it's in opaque binary, in JSON, in XML or whatever, a transient property will not be serialized. However, it changes semantic regarding frameworks, not language (provided that you see Java serialization as a framework).

These do change the semantic of the kotlin code :

- @Volatile : Allows an atomic access to a variable, it changes the way we think of multi-thread variable access. - @Synchronised : Will block the JVM until it can get a lock to access the code. This must be taken into account when calling such a method. I don't think you can see the JVM multi-threading as a framework. If you do access a synchornized method from different threads, no matter what you do or how you call it, the JVM will block the thread to wait for the lock. It's not a framework, it's a plateform feature.

So: I don’t think that annotations should be used to express features that are :

  • Standard to the language
  • Changes the semantic of the language code

Therefore, I believe that Volatile and Synchronised should be keywords :wink:


#19

I was looking at Kotlin's standard-library source the other day, and I stumbled upon https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Lazy.kt#L108:

@Volatile private var _value: Any? = UNINITIALIZED_VALUE

This makes me slightly sad.  Kotlin is a beautiful and smart language, and yet we have this ugly `@Volatile` annotation and `_backingField` naming (which looks like a form of Hungarian notation to me).

I think the Java equivalent would be something like this:

private volatile Object value = UNINITIALIZED;

It seems unfortunate when Java code is cleaner than the Kotlin equivalent.


#20

I sympathise with your emptions. But making each an every bit of Kotlin more pretty than each and every bit of Java is not a goal for us. The goal is to make Kotlin programs, on the whole, prettier. And there are trade-offs thereof, and we are weighing each one rather carefully. And we believe that this one has been decided the right way, despite the seeming (debatable and rather slight) unpleasantness of some (rare) bits of code.

Also, your comparison is not fair: in Java it would be

private volatile Object value = UNINITIALIZED_VALUE;

public T getValue() {
  …
}


So, where Kotlin’s stdlib has “value", Java has “getValue”, which is just more familiar, not prettier or less of “Hungarian notation”. Also, feel free to prefix your backing properties with “field” or some other word, if you prefer words to "”.