May 29, 2012
On 29-05-2012 23:32, Andrei Alexandrescu wrote:
> On 5/29/12 1:35 AM, deadalnix wrote:
>> Le 29/05/2012 01:38, Alex Rønne Petersen a écrit :
>>> I should probably add that Java learned it long ago, and yet we adopted
>>> it anyway... blergh.
>>>
>>
>> That is what I was about to say. No point of doing D if it is to repeat
>> previously done errors.
>
> So what is the lesson Java learned, and how does it address
> multithreaded programming in wake of that lesson?
>
> Andrei

It learned that allowing locking on arbitrary objects makes controlling locking (and thus reducing the chance for deadlocks) impossible.

It goes against our TLS-by-default and message-passing concurrency design.

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
May 29, 2012
On 29-05-2012 23:41, Andrei Alexandrescu wrote:
> On 5/29/12 5:29 AM, Dmitry Olshansky wrote:
>> Agreed, awfuly crippled design for a language with Thread-local by
>> default.
>> Looks like we have 'oh my god, what were they thinking' moment here.
>
> The synchronized class feature was copied by Walter from Java while he
> at the time had only little understanding of multithreading. The
> thread-local by default notion was added much later.
>
> Indeed we'd design things very differently if synchronized came about
> later. The design of "synchronized" described in TDPL and not yet
> implemented is a conciliation of the two.
>
>> If anything I'd rather re-implement the whole v-table infrastructure via
>> mixins, templates & friends.
>
> Could you please elaborate how that would help multithreading?
>
>
> Andrei
>

The whole concept of synchronization needs to be ripped out of Object with *extreme prejudice*. We have core.sync.mutex for a reason.

I think the vtable infrastructure as templates/mixins is an orthogonal point.

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
May 29, 2012
On 5/29/12 2:49 PM, Alex Rønne Petersen wrote:
> On 29-05-2012 23:32, Andrei Alexandrescu wrote:
>> On 5/29/12 1:35 AM, deadalnix wrote:
>>> Le 29/05/2012 01:38, Alex Rønne Petersen a écrit :
>>>> I should probably add that Java learned it long ago, and yet we adopted
>>>> it anyway... blergh.
>>>>
>>>
>>> That is what I was about to say. No point of doing D if it is to repeat
>>> previously done errors.
>>
>> So what is the lesson Java learned, and how does it address
>> multithreaded programming in wake of that lesson?
>>
>> Andrei
>
> It learned that allowing locking on arbitrary objects makes controlling
> locking (and thus reducing the chance for deadlocks) impossible.

And how does Java address multithreading in general, and those issues in particular, today?

Andrei

May 29, 2012
On 5/29/12 2:50 PM, Alex Rønne Petersen wrote:
> On 29-05-2012 23:41, Andrei Alexandrescu wrote:
>> On 5/29/12 5:29 AM, Dmitry Olshansky wrote:
>>> Agreed, awfuly crippled design for a language with Thread-local by
>>> default.
>>> Looks like we have 'oh my god, what were they thinking' moment here.
>>
>> The synchronized class feature was copied by Walter from Java while he
>> at the time had only little understanding of multithreading. The
>> thread-local by default notion was added much later.
>>
>> Indeed we'd design things very differently if synchronized came about
>> later. The design of "synchronized" described in TDPL and not yet
>> implemented is a conciliation of the two.
>>
>>> If anything I'd rather re-implement the whole v-table infrastructure via
>>> mixins, templates & friends.
>>
>> Could you please elaborate how that would help multithreading?
>>
>>
>> Andrei
>>
>
> The whole concept of synchronization needs to be ripped out of Object
> with *extreme prejudice*. We have core.sync.mutex for a reason.

That's more of a restating than an elaboration.

Andrei
May 29, 2012
On 29-05-2012 23:38, Andrei Alexandrescu wrote:
> On 5/29/12 5:26 AM, Alex Rønne Petersen wrote:
>> Generalized object monitors is the worst idea in programming language
>> and virtual machine design, ever.
>
> I think that's an exaggeration. Care to elaborate a bit?
>
> Andrei
>

1) You waste an entire word of memory in *every single object you ever create*. And for the majority of objects, this word will always be zero/null. Not to mention that when you do allocate a monitor, you also get the actual memory of that monitor in addition.
2) Anyone can lock on any object meaning it's near impossible to see where a deadlock might come from.
3) Encapsulation is completely broken as a result of (2).
4) There's a whole bunch of runtime and GC plumbing (which is even broken in druntime right now) to support object monitors. To fix this entire mess, we'd need weak references.
5) This whole object monitor model goes against our "thread-local by default" model. Synchronization is evil and should be explicit with core.sync.mutex.

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
May 29, 2012
On 29-05-2012 23:33, Andrei Alexandrescu wrote:
> On 5/29/12 1:37 AM, deadalnix wrote:
>> I would say that breaking things here, with the right deprecation
>> process, is the way to go.
>
> So what should we use for mutex-based synchronization if we deprecate
> synchronized classes?
>
> Andrei

core.sync.mutex

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
May 29, 2012
On 29-05-2012 23:31, Andrei Alexandrescu wrote:
> On 5/29/12 1:32 AM, deadalnix wrote:
>> I already did some comment about this.
>>
>> Making any object synchronization is a very bad design decision. It is
>> deadlock prone, it is liquid lock prone, it cause an overhead on any
>> object, and even worse, it is useless most of the time as D promote
>> thread locality (which is very good).
>
> Actually I think such a characterization is superficial and biased to
> the extent it becomes wrong.

Really ? I think he's spot on.

>
> Locks are deadlock-prone by design, whether used with objects or with
> classic procedural programs. In fact they are better confined to objects
> because the association between the protected data and the
> synchronization object is clear.

There is no doubt that synchronization of any kind is hard and error-prone no matter how you do it. But do you really think that the situation is made better by allowing locking on *anything*, meaning that locks are effectively resources shared publicly?

Besides, what's wrong with core.sync.mutex?

>
>> OOP and concurrency are 2 orthogonal topics, and should be handled as
>> such.
>
> Well there would be active objects that contradict that. But really it
> is very natural to encapsulate together some data along with the mutex
> that must be acquired to manipulate it. There are known issues with
> inheritance, but they are not introduced by the approach, only exposed
> by it.
>
>
> Andrei

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
May 29, 2012
On 29-05-2012 23:54, Andrei Alexandrescu wrote:
> On 5/29/12 2:49 PM, Alex Rønne Petersen wrote:
>> On 29-05-2012 23:32, Andrei Alexandrescu wrote:
>>> On 5/29/12 1:35 AM, deadalnix wrote:
>>>> Le 29/05/2012 01:38, Alex Rønne Petersen a écrit :
>>>>> I should probably add that Java learned it long ago, and yet we
>>>>> adopted
>>>>> it anyway... blergh.
>>>>>
>>>>
>>>> That is what I was about to say. No point of doing D if it is to repeat
>>>> previously done errors.
>>>
>>> So what is the lesson Java learned, and how does it address
>>> multithreaded programming in wake of that lesson?
>>>
>>> Andrei
>>
>> It learned that allowing locking on arbitrary objects makes controlling
>> locking (and thus reducing the chance for deadlocks) impossible.
>
> And how does Java address multithreading in general, and those issues in
> particular, today?
>
> Andrei
>

It doesn't, and neither does C#. Java still encourages using synchronized, and C# still encourages using lock, but many prominent figures in those programming language communities have written blog posts on why these language constructs are evil and should be avoided.

Besides, it seems to me that D can't quite make up its mind. We have TLS by default, and we encourage message-passing (through a library mechanism), and then we have the synchronized statement and attribute. It just seems so incredibly inconsistent. synchronized encourages doing the wrong thing (locks and synchronization).

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
May 29, 2012
On 30.05.2012 1:41, Andrei Alexandrescu wrote:
>> If anything I'd rather re-implement the whole v-table infrastructure via
>> mixins, templates & friends.
>
> Could you please elaborate how that would help multithreading?
>

It's unrelated to "ease multithreading part strictly speaking.
My observation is that it leads to at least removal of 1 word per object. Not a small thing if you happen to use GC, that "sells his memory" in 16 byte chunks, chances are you'd waste 2 instead of one.

Again strictly speaking I'm of an opinion that having mutex together with object guarded by it is at least better then 2 separate entities bound together by virtue of code comments :)
In any case if mutex is desired, object could have had some other base type say SyncObject. Or use "synchronized class" to that end, what it does now by the way - locks on each method?

More about the actual point is that I've come to believe that there is satisfactory way to implement whatever scheme of polymorphism* we want within the language on top of structs without 'class' and 'interface' keywords, special "flawed" pointer type (i.e. tail-const anyone?), and last but not least without new/delete/finalizes (new/delete are still overridable, hint-hint) madness.

Ideally I think it should be possible to lower the whole interface/object/class infrastructure to code that uses structs with direct function pointer tables, etc. Multiple alias this is the key, sadly so, otherwise subtyping to multiple interfaces seem not likely.
Then some future compiler may even chose to not provide OOP as built-in but lower to this manual implementation on top of struct(!).

*I like the one in Smalltalk or Obj-C. Also I think exposing type-tag as ordinal (index inside one global master v-table) instead of pointless _hidden_ v-table pointer could be interesting in certain designs. Another idea is to try tackling multi-methods via some form of compressed 2-stage v-table. (my recent work on generalized tries in D sparked some ideas)

-- 
Dmitry Olshansky
May 29, 2012
On 30.05.2012 1:48, Andrei Alexandrescu wrote:

>> I know. If anything it's hardly a good excuse, it should have been
>> revisited once TLS was introduced. It still can be now.
>
> Don't be hatin'. You'd appreciate the matter considerably more after you
> defined your own language and had its users yell at you for changing
> even a small detail.
>
> The situation is at it is, and there's no need to get agitated. We're
> all on the same boat, trying to make things work out best.
>

Yeah, no problem. I mean I wasn't bashing D for bugs or flaws (before this point at least) anyway. And it's not like I'm cursing here ;)

I just wish it was different. To set things straight I still believe that OOP part of language is not what I want it to be, and thus I just don't use it. Like in the old saying: there is always a choice.

Things that stopped me from using it are:
- hidden v-table design like C++ and Java, doesn't help things much. Encapsulation is good, but if it has no advantages other then hiding things it's bad.
- GCed by default, no provision for custom allocation until very recently
- monitor field, and who knows what else (v-table obviously, maybe something else?)
- no tail-const, no solutions aside from rebindable and casts. Pointers to struct look so much better in this regard ;)
- special slow built-in protocols for equality (it's robust, but I don't need it)
- opEquals signature madness (probably fixed?)
- final vs virtual, no inlining of known ahead-of-time virtual call
- nowdays enforced purity, @safe, nothrow whatever. Why should I follow these restrictions - dubious.

Observe that all of the above have very few advantages brought to me by compiler:
- polymorphism
- inheritance
- contract inheritance

Of the above I only ever needed the first 2. Call me dinosaur.

-- 
Dmitry Olshansky