Should literals be immutable ?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

Should literals be immutable ?

Clément Béra
Hey guys,

I was discussing about methods like that with Marcus:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr. 
#(false) at: 1 put: true ]
ifTrue: [ 'other calls' logCr ]

DoIt:
4 timesRepeat: [MyClass new foo ]

Transcript:
'first call since compilation'
'other calls'
'other calls'
'other calls'

In the sista image/VM, we are most probably going with Eliot to implement a slow path when you edit the literal of a method (deoptimizing the method) so we can consider that the literals are immutable in the generic case (and if it's not true, execution falls back to a slow path).

But I was thinking, does it worth it to implement the slow path ? 
Couldn't we just put literals as immutable all the time ? 
The methods as the one shown before would not work any more. But does someone use it ?

...

Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

ccrraaiigg

Hoi Clément--

     In keeping with the principle of least surprise (and least time
spent chasing strange bugs at 4am :), I would prefer that literals be
immutable.


     thanks,

-C

--
Craig Latta
netjam.org
+31   6 2757 7177 (SMS ok)
+ 1 415  287 3547 (no SMS)


Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Thierry Goubier
In reply to this post by Clément Béra


2014-10-27 16:22 GMT+01:00 Clément Bera <[hidden email]>:
Hey guys,

I was discussing about methods like that with Marcus:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr. 
#(false) at: 1 put: true ]
ifTrue: [ 'other calls' logCr ]

What, you can do that?
 

DoIt:
4 timesRepeat: [MyClass new foo ]

Transcript:
'first call since compilation'
'other calls'
'other calls'
'other calls'

In the sista image/VM, we are most probably going with Eliot to implement a slow path when you edit the literal of a method (deoptimizing the method) so we can consider that the literals are immutable in the generic case (and if it's not true, execution falls back to a slow path).

But I was thinking, does it worth it to implement the slow path ? 
Couldn't we just put literals as immutable all the time ?

I would do that, yes.
 
The methods as the one shown before would not work any more. But does someone use it ?

I could imagine cases where, in very tight memory situations or with very very large literals, we could make use of modifying literals. But I'm not sure this is a significant requirement, or that it couldn't be considered if there is a significant (and rich) use case to fund the development of the slow path.

Is there a critic rule catching that sort of thing? That we could run on Squeaksource / Smalltalkhub?

Thierry
 

...


Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Marcus Denker-5




I could imagine cases where, in very tight memory situations or with very very large literals, we could make use of modifying literals. But I'm not sure this is a significant requirement, or that it couldn't be considered if there is a significant (and rich) use case to fund the development of the slow path.

Pharos does not target classic “resource constraint” embedded scenarios.
(else we would do many things a lot different)
Is there a critic rule catching that sort of thing? That we could run on Squeaksource / Smalltalkhub?
No, as you can hand a reference to a literal over to someone else who does not even know that it is a literal… this dreaded “am I going insane, it’s writing right here” bug where you change a string somewhere that actually is a string literal of some method…

   Marcus
Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Eliot Miranda-2
In reply to this post by Clément Béra
Hi Clément,

     IMO literals should be immutable all the time, and IMO the best way is via a per-object isImmutable bit.  We implemented this for VW in 7.0 and it was a positive experience.

I've implemented the "create an immutable subclass for each literal class" and it has lots of problems (thing printString or storeString having to produce something like 'an immutable string' asMutableString for a normal string).

The per-object bit has other uses too.  Object databases such as GemStone can use it to make write-through scheme efficient.  

The only problem we had was dealing with broken user code that was depending on mutable literals.  This is of course a bug, but folks thought we shouldn't just break user code so Alan Knight provided a system setting that write a warning to the transcript and updated the literal instead if raising an error.  That's easy to do, if IMO dubious.  Fixing the code is as easy as sending copy to the literal in question, the copy being mutable.

Now I implemented the per-object bit for the Newspeak interpreter WM so you'll find lots of the necessary work is there waiting to be ported to the Stack and Cog VMs.  I've not had the time to implement the JIT support to catch inst var and at:put: writes to immutables.  But it shouldn't be too much work.  Volunteers?

Eliot (phone)

On Oct 27, 2014, at 8:22 AM, Clément Bera <[hidden email]> wrote:

Hey guys,

I was discussing about methods like that with Marcus:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr. 
#(false) at: 1 put: true ]
ifTrue: [ 'other calls' logCr ]

DoIt:
4 timesRepeat: [MyClass new foo ]

Transcript:
'first call since compilation'
'other calls'
'other calls'
'other calls'

In the sista image/VM, we are most probably going with Eliot to implement a slow path when you edit the literal of a method (deoptimizing the method) so we can consider that the literals are immutable in the generic case (and if it's not true, execution falls back to a slow path).

But I was thinking, does it worth it to implement the slow path ? 
Couldn't we just put literals as immutable all the time ? 
The methods as the one shown before would not work any more. But does someone use it ?

...

Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Eliot Miranda-2
In reply to this post by ccrraaiigg



On Oct 27, 2014, at 8:48 AM, Craig Latta <[hidden email]> wrote:


Hoi Clément--

    In keeping with the principle of least surprise (and least time
spent chasing strange bugs at 4am :), I would prefer that literals be
immutable.

Sleep no sleep no sleep no sleep and no mad
Video machine to eat time, a city scene
I can't explain, strange bugs alone at 4am
The insane alone at 4a.m, ...



    thanks,

-C

--
Craig Latta
netjam.org
+31   6 2757 7177 (SMS ok)
+ 1 415  287 3547 (no SMS)


Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Thierry Goubier
In reply to this post by Marcus Denker-5


2014-10-27 17:03 GMT+01:00 Marcus Denker <[hidden email]>:




I could imagine cases where, in very tight memory situations or with very very large literals, we could make use of modifying literals. But I'm not sure this is a significant requirement, or that it couldn't be considered if there is a significant (and rich) use case to fund the development of the slow path.

Pharos does not target classic “resource constraint” embedded scenarios.
(else we would do many things a lot different)
Is there a critic rule catching that sort of thing? That we could run on Squeaksource / Smalltalkhub?
No, as you can hand a reference to a literal over to someone else who does not even know that it is a literal… this dreaded “am I going insane, it’s writing right here” bug where you change a string somewhere that actually is a string literal of some method…

You mean, as in:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr ]
ifTrue: [ 'other calls' logCr ].
^ #(false)

in a workspace

4 timesRepeat: [MyClass new foo at: 1 put: true ]

Thierry
 

   Marcus

Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Levente Uzonyi-2
In reply to this post by Clément Béra
On Mon, 27 Oct 2014, Clément Bera wrote:

> Hey guys,
> I was discussing about methods like that with Marcus:
>
> MyClass>>foo
> #(false) first 
> ifFalse: [ 
> 'first call since compilation' logCr. 
> #(false) at: 1 put: true ]
> ifTrue: [ 'other calls' logCr ]
>
> DoIt:
> 4 timesRepeat: [MyClass new foo ]
>
> Transcript:
> 'first call since compilation'
> 'other calls'
> 'other calls'
> 'other calls'
>
> In the sista image/VM, we are most probably going with Eliot to implement a slow path when you edit the literal of a method (deoptimizing the method) so we can consider that the literals are immutable in the
> generic case (and if it's not true, execution falls back to a slow path).
>
> But I was thinking, does it worth it to implement the slow path ? 
> Couldn't we just put literals as immutable all the time ? 
> The methods as the one shown before would not work any more. But does someone use it ?
This method was used in Seaside (or some Seaside extension) to cache
the contents of files in methods.

By the way, how would you ensure immutability?
I mean, how would you prevent something like this?

MyClass >> foo

  ^#('placeHolder')

And then

(((MyClass >> #foo) literalAt: 1) at: 1) becomeForward: Object new


Levente

P.S.: why isn't this on the vm-dev list?

>
> ...
>
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Marcus Denker-4
In reply to this post by Thierry Goubier

On 27 Oct 2014, at 17:16, Thierry Goubier <[hidden email]> wrote:



2014-10-27 17:03 GMT+01:00 Marcus Denker <[hidden email]>:




I could imagine cases where, in very tight memory situations or with very very large literals, we could make use of modifying literals. But I'm not sure this is a significant requirement, or that it couldn't be considered if there is a significant (and rich) use case to fund the development of the slow path.

Pharos does not target classic “resource constraint” embedded scenarios.
(else we would do many things a lot different)
Is there a critic rule catching that sort of thing? That we could run on Squeaksource / Smalltalkhub?
No, as you can hand a reference to a literal over to someone else who does not even know that it is a literal… this dreaded “am I going insane, it’s writing right here” bug where you change a string somewhere that actually is a string literal of some method…

You mean, as in:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr ]
ifTrue: [ 'other calls' logCr ].
^ #(false)

in a workspace

4 timesRepeat: [MyClass new foo at: 1 put: true ]


Or even easier…

someMethod
^’oohhh’


MyClass new someMethod at: 1 put: $N.


the execute

MyClass new someMethod

and wonder where that string comes from.

Marcus
Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Eliot Miranda-2
In reply to this post by Levente Uzonyi-2


On Mon, Oct 27, 2014 at 9:19 AM, Levente Uzonyi <[hidden email]> wrote:
On Mon, 27 Oct 2014, Clément Bera wrote:

Hey guys,
I was discussing about methods like that with Marcus:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr. 
#(false) at: 1 put: true ]
ifTrue: [ 'other calls' logCr ]

DoIt:
4 timesRepeat: [MyClass new foo ]

Transcript:
'first call since compilation'
'other calls'
'other calls'
'other calls'

In the sista image/VM, we are most probably going with Eliot to implement a slow path when you edit the literal of a method (deoptimizing the method) so we can consider that the literals are immutable in the
generic case (and if it's not true, execution falls back to a slow path).

But I was thinking, does it worth it to implement the slow path ? 
Couldn't we just put literals as immutable all the time ? 
The methods as the one shown before would not work any more. But does someone use it ?

This method was used in Seaside (or some Seaside extension) to cache the contents of files in methods.

By the way, how would you ensure immutability?

See my message earlier in the thread.  Via a per-object isImmutable bit.

 
I mean, how would you prevent something like this?

MyClass >> foo

        ^#('placeHolder')

And then

(((MyClass >> #foo) literalAt: 1) at: 1) becomeForward: Object new


Levente

P.S.: why isn't this on the vm-dev list?


...





--
best,
Eliot
Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

Levente Uzonyi-2
On Mon, 27 Oct 2014, Eliot Miranda wrote:

> See my message earlier in the thread.  Via a per-object isImmutable bit.

So you would set the bit for the CompiledMethod and all literals
(including sub-arrays, floats, integers, booleans etc)?

Does the set immutability bit also mean that the object can't be swapped
with #become:?

Levente
Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

Eliot Miranda-2


On Mon, Oct 27, 2014 at 9:55 AM, Levente Uzonyi <[hidden email]> wrote:
 
On Mon, 27 Oct 2014, Eliot Miranda wrote:

See my message earlier in the thread.  Via a per-object isImmutable bit.

So you would set the bit for the CompiledMethod and all literals (including sub-arrays, floats, integers, booleans etc)?

The literals, definitely.  Whether the bit is set for compiled methods or not depends on things like how easy you want to be able to update the source pointer or add/delete properties.
 

Does the set immutability bit also mean that the object can't be swapped with #become:?

That's debatable, but in VW we disallowed two-way become: and allowed one-way become: for immutables.  I think that's right.  one-way become is about references to objects, two-way become is about changing objects themselves. 
 
--
best,
Eliot
Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

Levente Uzonyi-2
On Mon, 27 Oct 2014, Eliot Miranda wrote:

>>      So you would set the bit for the CompiledMethod and all literals (including sub-arrays, floats, integers, booleans etc)?
>
> The literals, definitely.  Whether the bit is set for compiled methods or not depends on things like how easy you want to be able to update the source pointer or add/delete properties.

If the CompiledMethod itself is not immutable, then one can replace its
top level literals easily.
If it's immutable, then the system will face all kind of problems, like
adding the correct trailer during method installation.
You could say "no problem, I'll use #becomeForward: to do it, it's fast in
Spur", but that's what my other question is about.


>>      Does the set immutability bit also mean that the object can't be swapped with #become:?
>
> That's debatable, but in VW we disallowed two-way become: and allowed one-way become: for immutables.  I think that's right.  one-way become is about references to objects, two-way become is about changing objects themselves. 

It's not about #become: vs #becomeForward:. If objects with the immutable
bit set can be replaced with #becomeForward:, then the literals are not
really immutable, because they can be changed.
If you also want to check if there are immutable objects pointing to the
object which is about to be replaced, then the performance of
#becomeForward: would become as slow as in the non-Spur VMs.

Levente
Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

Eliot Miranda-2


On Mon, Oct 27, 2014 at 12:12 PM, Levente Uzonyi <[hidden email]> wrote:
 
On Mon, 27 Oct 2014, Eliot Miranda wrote:

     So you would set the bit for the CompiledMethod and all literals (including sub-arrays, floats, integers, booleans etc)?

The literals, definitely.  Whether the bit is set for compiled methods or not depends on things like how easy you want to be able to update the source pointer or add/delete properties.

If the CompiledMethod itself is not immutable, then one can replace its top level literals easily.
If it's immutable, then the system will face all kind of problems, like adding the correct trailer during method installation.
You could say "no problem, I'll use #becomeForward: to do it, it's fast in Spur", but that's what my other question is about.

 
     Does the set immutability bit also mean that the object can't be swapped with #become:?

That's debatable, but in VW we disallowed two-way become: and allowed one-way become: for immutables.  I think that's right.  one-way become is about references to objects, two-way become is about changing objects themselves. 

It's not about #become: vs #becomeForward:. If objects with the immutable bit set can be replaced with #becomeForward:, then the literals are not really immutable, because they can be changed.
If you also want to check if there are immutable objects pointing to the object which is about to be replaced, then the performance of #becomeForward: would become as slow as in the non-Spur VMs.

I disagree.  It's about an incremental improvement.  A per-object immutability bit used to protect against direct alteration of literals works.  Right now there's nothing to stop one recompiling a method, and per-object immutability doesn't change that.  So mutable compiled methods are tolerable (at least in VW we kept methods mutable).  But having the bit stop direct modification of literals is goodness, not to be avoided because it doesn't prevent more exotic modification via become.

--
best,
Eliot
Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

Tudor Girba-2
In reply to this post by Eliot Miranda-2
Talking about 4 am, you should also watch the coolest talk about it:
http://www.ted.com/talks/rives_on_4_a_m


Cheers,
Doru



On Mon, Oct 27, 2014 at 5:13 PM, Eliot Miranda <[hidden email]> wrote:



On Oct 27, 2014, at 8:48 AM, Craig Latta <[hidden email]> wrote:


Hoi Clément--

    In keeping with the principle of least surprise (and least time
spent chasing strange bugs at 4am :), I would prefer that literals be
immutable.

Sleep no sleep no sleep no sleep and no mad
Video machine to eat time, a city scene
I can't explain, strange bugs alone at 4am
The insane alone at 4a.m, ...



    thanks,

-C

--
Craig Latta
netjam.org
+31   6 2757 7177 (SMS ok)
<a href="tel:%2B%201%20415%20%C2%A0287%203547" value="+14152873547" target="_blank">+ 1 415  287 3547 (no SMS)





--

"Every thing has its own flow"
Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

Levente Uzonyi-2
In reply to this post by Eliot Miranda-2
On Mon, 27 Oct 2014, Eliot Miranda wrote:

> I disagree.  It's about an incremental improvement.  A per-object
immutability bit used to protect against direct alteration of literals
works.  Right now there's nothing to stop one recompiling a method, and
per-object immutability doesn't change that.  So mutable compiled methods
are tolerable (at least in VW we kept methods mutable).  But having the
bit stop direct modification of literals is goodness, not to
be avoided because it doesn't prevent more exotic modification via become.

But if you allow mutable CompiledMethods, then Clement's example can be
rewritten as:

MyClass >> #foo

  ^#(false) first
  ifFalse: [
  'first call since compilation' logCr.
  thisContext method in: [ :method |
  (method literalAt: (method literals indexOf: #(false)) put: { true }) ] ]
  ifTrue: [ 'other calls' logCr ]

(#becomeForward: could also be used on the literal).

How will you avoid implementing the deoptimization of the method (the
slow path) when this is possible?

Levente
Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

ccrraaiigg
In reply to this post by Eliot Miranda-2

Craig> In keeping with the principle of least surprise (and least time
Craig> spent chasing strange bugs at 4am :), I would prefer that
Craig> literals be immutable.

Eliot> Sleep no sleep no sleep no sleep and no mad
Eliot> Video machine to eat time, a city scene
Eliot> I can't explain, strange bugs alone at 4am
Eliot> The Seine alone at 4a.m, ...

     Cool, King Crimson agrees with us. :)

Tudor> Talking about 4 am, you should also watch the coolest talk about
Tudor> it: [1]. And a beautiful follow up: [2]

     Thanks! I'll save these for 4am, not long now...


-C


[1] http://www.ted.com/talks/rives_on_4_a_m
[2] http://www.ted.com/talk/rives_a_museum_of_4_o_clock_in_the_morning

--
Craig Latta
netjam.org
+31   6 2757 7177 (SMS ok)
+ 1 415  287 3547 (no SMS)


Reply | Threaded
Open this post in threaded view
|

Re: Should literals be immutable ?

stepharo
In reply to this post by Clément Béra
I think that immutable literals is the way to go.

Hey guys,

I was discussing about methods like that with Marcus:

MyClass>>foo
#(false) first 
ifFalse: [ 
'first call since compilation' logCr. 
#(false) at: 1 put: true ]
ifTrue: [ 'other calls' logCr ]

DoIt:
4 timesRepeat: [MyClass new foo ]

Transcript:
'first call since compilation'
'other calls'
'other calls'
'other calls'

In the sista image/VM, we are most probably going with Eliot to implement a slow path when you edit the literal of a method (deoptimizing the method) so we can consider that the literals are immutable in the generic case (and if it's not true, execution falls back to a slow path).

But I was thinking, does it worth it to implement the slow path ? 
Couldn't we just put literals as immutable all the time ? 
The methods as the one shown before would not work any more. But does someone use it ?

...


Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

stepharo
In reply to this post by Levente Uzonyi-2
Levente

I think that the first step is not to make sure that you cannot hack inside literals like mad. But to make sure that in
99.99 % of the case, this assumption can hold.

Stef

On 27/10/14 15:46, Levente Uzonyi wrote:
 


On Mon, 27 Oct 2014, Eliot Miranda wrote:

I disagree.  It's about an incremental improvement.  A per-object
immutability bit used to protect against direct alteration of literals works.  Right now there's nothing to stop one recompiling a method, and
per-object immutability doesn't change that.  So mutable compiled methods are tolerable (at least in VW we kept methods mutable).  But having the bit stop direct modification of literals is goodness, not to
be avoided because it doesn't prevent more exotic modification via become.

But if you allow mutable CompiledMethods, then Clement's example can be rewritten as:

MyClass >> #foo

    ^#(false) first
        ifFalse: [
            'first call since compilation' logCr.
            thisContext method in: [ :method |
                (method literalAt: (method literals indexOf: #(false)) put: { true }) ] ]
        ifTrue: [ 'other calls' logCr ]

(#becomeForward: could also be used on the literal).

How will you avoid implementing the deoptimization of the method (the slow path) when this is possible?

Levente

Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Re: Should literals be immutable ?

Levente Uzonyi-2
On Mon, 27 Oct 2014, stepharo wrote:

> Levente
>
> I think that the first step is not to make sure that you cannot hack inside literals like mad. But to make sure that in 99.99 % of the case, this assumption can hold.
>
> Stef

Let me sum it up:
- The question is (by Clement) if it's okay to not implement the slow path.
- He assumes that he doesn't have to implement it if literals are immutable.
- I showed that literal immutability is not enough to avoid impelemting
the slow path.

So immutability doesn't affect anything about the need for implementing of
the slow path. If Clement decides to not implement it, then one will be
able to get the system into an inconsistent state, no matter if literals
are immutable or not.

If you think that the inconsistent state can only be achieved by hacking,
then here's another example without "hacks":

SomeClass >> #someMethod

  ^someBoolean
  ifTrue: [ Array with: 1 with: 2 with: 3 ]
  ifFalse: [ Array empty "we don't have to create a new array for this, right? :)" ]

SomeOtherClass >> #someOtherMethod: anArray

  | copy |
  anArray doSomething.
  "let's grow that array"
  copy := Array new: anArray size.
  copy replaceFrom: 1 to: anArray size with: anArray startingAt: 1.
  anArray becomeForward: copy. "this is really cheap in Spur (and in VW; even collections and streams use it)"

ThirdClass >> #yetAnotherMethod: aSomeClass

  | x |
  x := aSomeClass someMethod.
  y someOtherMethod: x.
  ^x

And now, when #yetAnotherMethod: is sent, we swap the literal in Array
class >> #empty with another non-empty array. Based on Eliot's comment
about VW, I'm pretty sure the VW guys had problems like this, despite of
having immutable literals.

Levente