May 30, 2012
On 2012-05-30 12:21, Dmitry Olshansky wrote:

> Yup. Even Java has Class as object. If D copied most of OOP from Java it
> seems strange that it was replaced by halfhearted factory method in
> object.di
> kind of saying "you have RTTI but it's crippled just in case if it is of
> no use".

I would not compare Java's Class with classes in Objective-C and Ruby. In Objective-C and Ruby classes are objects with their own hierarchy of inheritance. Example in Ruby:

class Foo
  def bar
    @instance_variable = 1
    @@class_variable = 2
  end

  # class method
  def self.bar
    @instance_variable_on_the_class = 1
    @@class_variable = 3
  end
end

a = Foo
instance_of_foo = a.new

> Still I'd think it's reasonable course to enact slow but steady
> evolution of existing OOP design, changing synchronized/monitors seems
> as good start as any.

I agree.

-- 
/Jacob Carlborg
May 30, 2012
On 30.05.2012 17:58, Jacob Carlborg wrote:
> On 2012-05-30 12:21, Dmitry Olshansky wrote:
>
>> Yup. Even Java has Class as object. If D copied most of OOP from Java it
>> seems strange that it was replaced by halfhearted factory method in
>> object.di
>> kind of saying "you have RTTI but it's crippled just in case if it is of
>> no use".
>
> I would not compare Java's Class with classes in Objective-C and Ruby.
> In Objective-C and Ruby classes are objects with their own hierarchy of
> inheritance. Example in Ruby:
>
> class Foo
> def bar
> @instance_variable = 1
> @@class_variable = 2
> end
>
> # class method
> def self.bar
> @instance_variable_on_the_class = 1
> @@class_variable = 3
> end
> end
>
> a = Foo
> instance_of_foo = a.new
>

Yes, it's OOP classics I think. Dynamic languages are easier in this regard. But let's not detract from original topic - locking.


-- 
Dmitry Olshansky
May 30, 2012
On 05/30/12 10:17, Thiez wrote:
> On Wednesday, 30 May 2012 at 06:31:32 UTC, Dmitry Olshansky wrote:
>> I'll intervene. Following famous nerd motto "talk is cheap, show me the code" I strongly suggest discussing concrete scenarios. All "prone to deadlock sentiment" is trivia as in buyer beware.
>>
>> That being said:
>>
>> class iMutexed
>> {//implicit intent, explicit details
>>     Mutex mutex;
>> }
>>
>> vs
>>
>> class iMutexed
>> {//implicit intent, implicit details
>>
>> //implicit mutex
>> }
>>
>> Makes little to no difference. EXCEPT that designer of the class has no control what so ever over hidden mutex! Thus I believe the best way to fix it is to (say) require obj be a subclass of Mutex in order to use synchronized(obj). i.e.
>>
>> class iMutexed: Mutex
>> {//explicit intent, implicit details
>>
>> }
> 
> Forcing synchronized classes to extend Mutex would make it impossible to create a synchronized subclass of a pre-existing unsynchronized class, would it not? Unless you want to introduce multiple inheritence. If you want to go this way at least make it an interface that has special meaning to the compiler, instead of a class that must be inherited from.
> 

Yeah, that leads to multiple inheritance, or cheap imitations thereof.

Just create lock+unlock (or acquire+release, whatever) operators that
can be overloaded, ie

void opUnary!("lock")() {}
void opUnary!("unlock")() {}

and make 'synchronize (struct|class){...}' use them, ditto for implicitly synchronized public methods (note: by lowering directly. You can already do something like this right now, but it would have a large runtime overhead.) Full backward compatibility retained, and the user can actually implement the locking in a sane manner - something that is not likely to happen any time soon as a built-in language feature. And yes, removing the monitors from classes is desirable and would have back-compat issues, but it is then just an optimization, and does not need to happen immediately.


On 05/30/12 11:34, deadalnix wrote:
> Le 29/05/2012 23:33, Andrei Alexandrescu a écrit :
>> 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
> 
> I think something similar to range design here is the way to go.
> 
> It is easy to define something like
> 
> template isLockable(T) {
>     enum isLockable = isShared!T && is(typeof(T.init.lock())) && is(typeof(T.init.release()));
> }
> 
> And allow locking only if(isLockable!Type) .
> 
> Now we can create SyncObject or any structure we want. The point is that we lock explicit stuff.

Yep, a way to mark objects usable for locking would also help. I'd drop the 'isShared' check - it's redundant and doesn't play well with D 'shared' concept in its current form; it can always be introduced back, when 'shared' evolves to something more sane.


On 05/30/12 11:14, Jacob Carlborg wrote:
> On 2012-05-29 23:48, Andrei Alexandrescu wrote:
> 
>> 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.
> 
> It seems more and more that D2 is not a designed language. Instead new features are just slapped on without considering how it would impact the rest of the language.

Yes. But it is not so much the adhoc features that are the problem, as long as they don't significantly break the rest of the language. Not fixing known deficiencies in the name of backward compatibility is. The gain from having a new, useful and working feature more than makes up for the cost of changes to app code. Having features that work for trivial hello-world programs, but fail for most real code, does much more harm than good.


artur
May 30, 2012
On 5/30/12 2:14 AM, Jacob Carlborg wrote:
> On 2012-05-29 23:48, Andrei Alexandrescu wrote:
>
>> 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.
>
> It seems more and more that D2 is not a designed language. Instead new
> features are just slapped on without considering how it would impact the
> rest of the language.

What features are you referring to?

Andrei

May 30, 2012
On 5/30/12 2:34 AM, deadalnix wrote:
> Le 29/05/2012 23:33, Andrei Alexandrescu a écrit :
>> 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
>
> I think something similar to range design here is the way to go.
>
> It is easy to define something like
>
> template isLockable(T) {
> enum isLockable = isShared!T && is(typeof(T.init.lock())) &&
> is(typeof(T.init.release()));
> }
>
> And allow locking only if(isLockable!Type) .
>
> Now we can create SyncObject or any structure we want. The point is that
> we lock explicit stuff.

But in this design anyone can lock such an object, which was something you advocated against.

Andrei
May 30, 2012
On 5/30/12 2:40 AM, deadalnix wrote:
> Le 30/05/2012 00:50, Andrei Alexandrescu a écrit :
>> On 5/29/12 2:59 PM, Alex Rønne Petersen wrote:
>>> 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.
>>
>> I'd be glad to think the same. Good arguments might be helpful. So far
>> I've seen exaggerated statements and posturing. Such are entertaining
>> but are of limited effectiveness in furthering a point.
>>
>
> I have provided link in other posts to document the point. Not to
> mention that my personal experience back up this too.
>
> I don't like what you are doing here, because you don't provide anything
> and simply discard what don't fit the current design. For instance, you
> didn't even considered the liquid lock problem.

It would help if I knew what a liquid lock is. The first page of Google search doesn't help.

I'm only discarding content-less posturing a la "worst idea ever", "useless", "very bad design decision" etc. Such is just immature. It is encouraging you have started sending content (the msdn paper), thanks, and please keep it coming.


Thanks,

Andrei
May 30, 2012
On Mon, 28 May 2012 18:36:13 -0400, Alex Rønne Petersen <alex@lycus.org> wrote:

> Hi,
>
> I've seen several occurrences of synchronized (this) and synchronized (this.classinfo) in both druntime and phobos by now. I propose that we officially ban these patterns with extreme prejudice.
>
> 1) Locking on the object instance is a HORRIBLE IDEA. Anyone who happens to use the object for locking will most likely end up with a deadlock on their hands.
> 2) Locking on something as fundamental as type info means that any arbitrary part of the application could cause a deadlock by doing the same.

Reading all of this thread, I think I agree with you.

Now, I'll point out a couple things:

1. Most code that is written with synchronized is *not* abused.
2. using synchronized(x) should not go away.

Here is what I'm thinking:

First, we give control of the mutex to the author of the type.  So instead of an implicit location, and implicit lazy construction, we make everything explicit:

Currently:

synchronized(x)
{
}

translates to

if(x.mutex == null)
   x.mutex = createmutex();
lock(x.mutex)
try
{
}
finally
{
   unlock(x.mutex);
}

So I propose it does this instead (entirely via lowering):

synchronized(x)
{
}

translates to:

auto l = x.__getMutex();
l.lock();
try
{
}
finally
{
   l.unlock();
}

So we have a few benefits here:

1. if typeof(x) does not define __getMutex, the above is a compiler error.
2. We can control the visibility of the mutex, because we can attribute private or protected to the __getMutex function.
3. We can control what kinds of objects of typeof(x) use mutexes.  e.g. define __getMutex() shared only.
4. We have control over the type of the lock, it can be anything and does not need to fit into some runtime function's interface.

Second, I propose a somewhat gradual transition.  I think on first release, we *still* keep the allocated word in the class instance, and keep the druntime function which lazily allocates the mutex.  Therefore, changing an object that currently *properly* can use synchronized(this) can be made to work:

class Synchronizable
{
   xxx __getMutex() shared { return _d_getObjectMonitor(this);}
}

We can deprecate that functionality, and the allocated space for the mutex later if desired.

-Steve
May 30, 2012
On 5/30/12 5:32 AM, Regan Heath wrote:
> On Wed, 30 May 2012 10:21:00 +0100, deadalnix <deadalnix@gmail.com> wrote:
>> You don't want to synchronize on ANY object. You want to synchronize
>> on explicit mutexes.
>
> +1 .. this is the key point/issue.

TDPL's design only allows for entire synchronized classes (not separate synchronized and unsynchronized methods), which pair mutexes with the data they protect. This is more restrictive than exposing mutexes, but in a good way. We use such a library artifact in C++ at Facebook all the time, to great success.

People shouldn't create designs that have synchronized classes referring to one another naively. Designing with mutexes (explicit or implicit) will always create the possibility of deadlock, so examples how that could happen are easy to come across.

TDPL improves on deadlocks by introducing synchronized statements with more than one argument, see 13.15.

The implicit mutexes used by synchronized classes are recursive.


Andrei

May 30, 2012
On 5/30/12 5:43 AM, deadalnix wrote:
> It doesn't address most of the drawback cited. Notably the fact that
> every object have a monitor field, but most of them will not use it.

Once the TDPL design is implemented, we could look into eliminating that field for all but "synchronized" class. In the TDPL design, "synchronized" is a class-level attribute.

Andrei
May 30, 2012
On 5/30/12 5:43 AM, Regan Heath wrote:
> Not in all cases. If you have a synchronized class, say a thread-safe
> container, which has a synchronized lookup and synchronized insert
> function, code like..
>
> if (!o.lookup(...))
> o.insert(...)
>
> obtains and releases the mutex twice each, and in between another thread
> could call o.insert() and insert the missing object - resulting in 2
> copies being inserted (or a duplicate insert exception being throw,
> or....).
>
> Now, the above is bad design, but the only other choice is to expose
> lock() and unlock() methods (or the mutex - back to square 1one) to
> allow locking around multiple class, but now the caller has to be
> careful to call unlock in all code paths. scope helps us in D, but this
> is actually the whole point of synchronized blocks - 1. ensuring the
> unlock always happens and 2. making the scope of the locking visible in
> the code.

There's a hybrid design available - a lock() operation may return a Locked!Type object which has a richer set of primitives. The implied understanding is that for the lifetime of that object, the underlying mutex is locked.

Andrei


3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19