October 15, 2015
On Thursday, 15 October 2015 at 14:28:00 UTC, Andrei Alexandrescu wrote:
> On 10/14/15 9:24 AM, Jacob Carlborg wrote:
>> On 2015-10-13 14:56, Dicebot wrote:
>>
>>> I still have no idea why I would ever use `synchronized` (any automatic
>>> thread synchronization is harmful in my opinion) so change itself is
>>> irrelevant. But it may break quite some old Java-liked 3d party code for
>>> no benefit and that would be annoying.
>>
>> Like DWT :)
>
> That may be worrisome. Any information on how many are using DWT, and how badly it would break if we pulled the change?
>
> If we assess there's too much breakage, we can define a DIP and make the check opt-in via a flag -dipNN.

The current behavior needs to be deprecated first regardless. We shouldn't just throw a switch and make it illegal to access member variables of a synchronized class. If we start with a deprecation message about it and only move to making it illegal later, then it really shouldn't matter much what existing code is doing.

A switch makes sense if we intend to experiment with this rather than necessarily being the case that we want to go in this direction. But even then, we'd have to either deprecate the current behavior at some point or just break everyone's code who hadn't bothered to use the switch.

But given the general hostility to synchronized in this thread (for whatever that's worth given the relatively few people involved), it could very well be that future discussions on shared/synchronized would lean towards just axing synchronized, making any changes we do to it now moot. Still, from a correctness standpoint, I think that it's pretty clear that synchronized classes are better than synchronized functions.

- Jonathan M Davis
October 15, 2015
On 10/15/15 4:06 AM, Dmitry Olshansky wrote:
> On 13-Oct-2015 21:28, Marco Leise wrote:
>> Am Tue, 13 Oct 2015 12:52:55 +0000
> [snip]
>> Guys, sorry to break into your wishful thinking, but
>>
>>     synchronized(mutex) {}
>>
>> already works as you want it to since as long as I can think.
>> Yes, it takes a parameter, yes it calls lock/unlock on the
>> mutex. :)
>
> Now how hard would it be to support any object (mutex kind) with
> lock/unlock?

AFAIK, the way this works is that mutex makes itself its own monitor object. So any other mutex-like object can follow this pattern. Or did I misunderstand the question?

> Or do we even need it with scope(exit)?

I personally find the enforcement of the scope guard much better than having to manually add the lock inside the scope. It gives a clear delineation of where the lock MUST go (before the scope is entered).

you can simulate synchronized(mutex) {} like:

{
   auto _lock = scopeLock(mutex);
}

But this is not nearly as robust. One could add junk before the lock, and no complaints from the compiler.

I don't really understand all the disdain for synchronized here...

-Steve
October 15, 2015
On 10/13/15 2:58 AM, Andrei Alexandrescu wrote:
> https://github.com/D-Programming-Language/dmd/pull/5188 implements a
> rule defined in TDPL: synchronized classes shall have no public members.

When I first read this, I thought "how the hell will you use this thing then?"

Then after reading through most of this thread, I realize you mean public *field* members. Public *method* members are allowed, right?

-Steve
October 15, 2015
On Thursday, 15 October 2015 at 15:20:25 UTC, Steven Schveighoffer wrote:
> On 10/13/15 2:58 AM, Andrei Alexandrescu wrote:
>> https://github.com/D-Programming-Language/dmd/pull/5188 implements a
>> rule defined in TDPL: synchronized classes shall have no public members.
>
> When I first read this, I thought "how the hell will you use this thing then?"
>
> Then after reading through most of this thread, I realize you mean public *field* members. Public *method* members are allowed, right?

Yes. The idea is that with a synchronized class, all access to the object must be via its member functions so that you can't bypass the mutex that protects the object. Then, because the compiler knows that nothing else can have direct access to the class' member variables and that they're protected by a mutex when inside of a member function, it's able to strip the outer layer of shared from the member variables when you operate on them. So, for basic cases at least, we don't have to cast away shared to operate on shared data - though for more complicated cases (e.g. stuff where stripping off the outer layer of shared isn't enough), you'd still have to cast away shared (though at least, it still encapsulates the shared data on some level in that case).

- Jonathan M Davis
October 15, 2015
On Thursday, 15 October 2015 at 15:16:59 UTC, Steven Schveighoffer wrote:
> I don't really understand all the disdain for synchronized here...

Because it's usually either the wrong solution or just unnecessary. If you are going to have a single mutex for an entire object, then it's nice in the way that it's nice that invariants are nice. They're not at all necessary, because the same thing can be done manually via assertions inside of all of the public member functions, but it does make them less error-prone.

However, it's frequently the case that having a mutex per class object is the wrong way to go. Usually, it's better to have tighter locks than that which target specific member variables rather than the class as a whole, and when you do want it at the class level, it's frequently better to have the user of the class do the locking, since in that case, there's a decent chance that the object is a member variable inside of another class/struct where it and another set of variables need to share a mutex, so having a mutex built into the object is redundant and causes unnecessary overhead. Having the mutex at the class level is simply too inflexible and arguably encourages bad coding practices. So, having synchronized on classes or functions is of questionable value and arguably harmful - though the fact that having it on classes would allow us to strip away the outer layer of shared on the class' members does add some value.

Ultimately, the only advantages to synchronized classes IMHO are:

1. They makes porting Java code easier.
2. Similar to how invariant helps with assertions at the class level, they make it easier to use a mutex at the class level correctly (though IMHO, that's almost always the wrong design).
3. They give us a way to implicitly cast away shared on some level (though not necessarily enough to be worth it).

So, they add _some_ value, but I'm not at all convinced that they're worth it.

As for synchronized statements/blocks, they're simply syntactic sugar that add no real value that I can see, and they can do less then the equivalent with guards/autolocks. These two pieces of code are equivalent:

synchronized(mutex)
{
}

{
    Guard guard(mutex);
}

and the second one is far more flexible, since it allows for stuff like guard.unlock() or using the mutex with a condition variable. So, synchronized statements are a poor-man's guard/autolock and simply not worth having IMHO unless we find some way that it allows us to get the compiler to do stuff (like being able to implicitly remove shared on some level, though because you have arbitrary code within the synchronized block and aren't dealing with encapsulated shared variables like with synchronized classes, I don't see how we really can get the compiler to do much special with synchronized blocks).

I don't know that it's worth it to remove synchronized from the language, but certainly, if we were starting from scratch, I'd be arguing against it. I think that it's a Java-ism that shouldn't have been ported to D. Java and C# had no choice, because they don't have proper RAII or scope statements, but we don't have that problem.

- Jonathan M Davis
October 15, 2015
On 10/15/15 6:20 PM, Steven Schveighoffer wrote:
> On 10/13/15 2:58 AM, Andrei Alexandrescu wrote:
>> https://github.com/D-Programming-Language/dmd/pull/5188 implements a
>> rule defined in TDPL: synchronized classes shall have no public members.
>
> When I first read this, I thought "how the hell will you use this thing
> then?"
>
> Then after reading through most of this thread, I realize you mean
> public *field* members. Public *method* members are allowed, right?
>
> -Steve

Right, sorry for the confusion. -- Andrei
October 15, 2015
On 10/15/15 11:50 AM, Jonathan M Davis wrote:
> On Thursday, 15 October 2015 at 15:16:59 UTC, Steven Schveighoffer wrote:
>> I don't really understand all the disdain for synchronized here...
>
> Because it's usually either the wrong solution or just unnecessary. If
> you are going to have a single mutex for an entire object, then it's
> nice in the way that it's nice that invariants are nice. They're not at
> all necessary, because the same thing can be done manually via
> assertions inside of all of the public member functions, but it does
> make them less error-prone.

I'm speaking just about the synchronized(mutex) statement, not about synchronized classes or methods.

I don't think I've ever used a synchronized class.

> As for synchronized statements/blocks, they're simply syntactic sugar
> that add no real value that I can see, and they can do less then the
> equivalent with guards/autolocks. These two pieces of code are equivalent:
>
> synchronized(mutex)
> {
> }
>
> {
>      Guard guard(mutex);
> }
>
> and the second one is far more flexible, since it allows for stuff like
> guard.unlock() or using the mutex with a condition variable.

You can do both of these with synchronized statements, mutex.unlock works and you can use with a condition variable.

As I said before, with the Guard lock, you have more room for error, and visually the lock is better identified with a synchronized statement.

> So,
> synchronized statements are a poor-man's guard/autolock and simply not
> worth having IMHO unless we find some way that it allows us to get the
> compiler to do stuff (like being able to implicitly remove shared on
> some level, though because you have arbitrary code within the
> synchronized block and aren't dealing with encapsulated shared variables
> like with synchronized classes, I don't see how we really can get the
> compiler to do much special with synchronized blocks).

We could do away with new, and require people to call malloc and constructors directly. The sugar is nice, and keeps your code from making dumb mistakes.

I see synchronized blocks as a clear advantage over arbitrary locking just from a cleanliness point of view.

-Steve
October 15, 2015
On Thursday, 15 October 2015 at 17:10:23 UTC, Steven Schveighoffer wrote:
> You can do both of these with synchronized statements, mutex.unlock works and you can use with a condition variable.
>
> As I said before, with the Guard lock, you have more room for error, and visually the lock is better identified with a synchronized statement.

There is no argument it looks nicer but it is a dedicated language feature for a minor syntax sugar over a feature that is almost never used in modern idiomatic concurrent code. Explicit locking in application code is rather old school, same as the notion that blindly adding more threads to existing OOP program helps to improve performance.

It isn't bad to have it but don't forget Andrei's words about constant sum of language complexity - we could have had something more useful in totally different domain instead of it.
October 15, 2015
On 2015-10-15 16:28, Andrei Alexandrescu wrote:

> That may be worrisome. Any information on how many are using DWT, and
> how badly it would break if we pulled the change?

I have no idea how many are using DWT. At least a couple of developers, i.e. that I see in PR's and on the forum.

grep returned 238 occurrences of "synchronized", this include both synchronized methods and the synchronized statement. Very few false positives (two that I know for sure).

-- 
/Jacob Carlborg
October 15, 2015
On Thursday, 15 October 2015 at 10:11:06 UTC, Andrei Alexandrescu wrote:
> For a while we were of the opinion that we should let "synchronized" and "shared" be and move on with alternative features. Now we believe an incomplete language definition is damaging the language as a whole so we better make them fully defined and useful within their charter.
>
> Lock-based synchronization has plenty of good uses and the scope locking defined by "synchronized" covers a large useful subset of it. We need to make it usable safely and without contortions, and this particular PR is a step along that way.
>
> It's not a huge priority but since Andrej has only done the work, the main concern left is breakage of existing code, albeit much of that is incorrect or unnecessarily unsafe.
>

I'm on board with that. Half baked feature is really the worse. We should really make sure we either don't do something or do it well.