Transcript error when forceUpdate: false (?)

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

Transcript error when forceUpdate: false (?)

Jaromir Matas
I ran the following in Squeak 5.3:

TranscriptStream forceUpdate: false.
[ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39

When the Transcript window fills up, 'Message not understood' and 'Assertion
failure' appear. I'm wondering is this a bug? Thanks. Jaromir

<http://forum.world.st/file/t372955/Screenshot_2021-01-22_205244.png>





--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

^[^ Jaromir
Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

David T. Lewis
On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:

> I ran the following in Squeak 5.3:
>
> TranscriptStream forceUpdate: false.
> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>
> When the Transcript window fills up, 'Message not understood' and 'Assertion
> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>
> <http://forum.world.st/file/t372955/Screenshot_2021-01-22_205244.png>
>

It certainly does appear to be a bug.

Dave


Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

timrowledge


> On 2021-01-22, at 5:26 PM, David T. Lewis <[hidden email]> wrote:
>
> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>> I ran the following in Squeak 5.3:
>>
>> TranscriptStream forceUpdate: false.
>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>
>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>
>> <http://forum.world.st/file/t372955/Screenshot_2021-01-22_205244.png>
>>
>
> It certainly does appear to be a bug.

We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?


tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
The hardness of the butter is proportional to the softness of the bread.



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Levente Uzonyi
On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis <[hidden email]> wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>> <http://forum.world.st/file/t372955/Screenshot_2021-01-22_205244.png>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.

Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
It's simply not thread-safe.

Hmm... in particular, there is a side effect (i.e. layout update) in the window's pluggable text morph. Even if #forceUpdate is false.

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.

Best,
Marcel

Am 23.01.2021 21:05:05 schrieb Levente Uzonyi <[hidden email]>:

On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Christoph Thiede

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.


But wouldn't violate that the #forceUpdate preference when a non-UI process blocks the image for a while?


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Montag, 25. Januar 2021 12:25:56
An: squeak-dev
Betreff: Re: [squeak-dev] Transcript error when forceUpdate: false (?)
 
It's simply not thread-safe.

Hmm... in particular, there is a side effect (i.e. layout update) in the window's pluggable text morph. Even if #forceUpdate is false.

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.

Best,
Marcel

Am 23.01.2021 21:05:05 schrieb Levente Uzonyi <[hidden email]>:

On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
But wouldn't violate that the #forceUpdate preference when a non-UI process blocks the image for a while?

... "violate" is such a strong word. ^__^ I know that we have a "lock" in Transcript to at least not mix up the buffer contents. "forceUpdate" could be enabled by adding an extra semaphore to let the non-ui process sleep until that deferred UI message got processed.

Best,
Marcel

Am 25.01.2021 13:24:51 schrieb Thiede, Christoph <[hidden email]>:

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.


But wouldn't violate that the #forceUpdate preference when a non-UI process blocks the image for a while?


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Montag, 25. Januar 2021 12:25:56
An: squeak-dev
Betreff: Re: [squeak-dev] Transcript error when forceUpdate: false (?)
 
It's simply not thread-safe.

Hmm... in particular, there is a side effect (i.e. layout update) in the window's pluggable text morph. Even if #forceUpdate is false.

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.

Best,
Marcel

Am 23.01.2021 21:05:05 schrieb Levente Uzonyi <[hidden email]>:

On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Jaromir Matas
> It's simply not thread-safe.

Are you saying whenever the UI process interrupts Transcript printing from a
lower-priority non-UI process a failure occurs?


I tried     [ 20 timesRepeat: [ 1000 factorial . Transcript show: 'x' ]  ]
forkAt: 39      a few times while having the Transcript window full and
sooner rather than later it failed *regardless *of the #forceUpdate setting.

> In #endEntry, we could check for "Processor activeProcess == Project
> current uiProcess" to then use "Project current addDeferredUIMessage..."
> automatically.

Deferring automatically would actually mean #forceUpdate = true no longer
means anything for lower-priority non-UI processes, right? (Because the
problem seems to occur regardless of #forceUpdate setting)

It still seems to me it has something to do with scrolling the full
Transcript window...

Regards,
Jaromir



--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

^[^ Jaromir
Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Jaromir Matas
Well, I tried deferring the whole Transcript endEntry machinery to the UI
doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
#forceUpdate = false only ... and it seems to avoid the problem!

TranscriptStream >> endEntry

        deferredEntry ifNil: [ false ].    "this is a new instance variable"
        self semaphore critical:[
                self class forceUpdate
                        ifTrue: [self changed: #appendEntry; reset]
                        ifFalse: [deferredEntry := true].


TranscriptStream >> flushDeferredEntry
        "This is run every UI cycle in doOneCycleNowFor:"

        deferredEntry ifTrue: [
                self class forceUpdate: true.
                self endEntry.
                deferredEntry := false.
                self class forceUpdate: false.
                ]

doOneCycleNowFor: aWorld
       
        "... the whole body remains unchanged except:"
       
        capturingGesture ifFalse:
                [aWorld runStepMethods.
                Transcript flushDeferredEntry.     "this is printing for #forceUpdate =
false"
                self displayWorldSafely: aWorld].


For #forceUpdate = true the endEntry mechanism remains unchanged and failing
as before...



--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

^[^ Jaromir
Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
Hi Jaromir,

please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)

@all: Would Transcript now be thread-safe? Did I miss something?

Best,
Marcel

Am 25.01.2021 21:38:36 schrieb jaromir <[hidden email]>:

Well, I tried deferring the whole Transcript endEntry machinery to the UI
doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
#forceUpdate = false only ... and it seems to avoid the problem!

TranscriptStream >> endEntry

deferredEntry ifNil: [ false ]. "this is a new instance variable"
self semaphore critical:[
self class forceUpdate
ifTrue: [self changed: #appendEntry; reset]
ifFalse: [deferredEntry := true].


TranscriptStream >> flushDeferredEntry
"This is run every UI cycle in doOneCycleNowFor:"

deferredEntry ifTrue: [
self class forceUpdate: true.
self endEntry.
deferredEntry := false.
self class forceUpdate: false.
]

doOneCycleNowFor: aWorld

"... the whole body remains unchanged except:"

capturingGesture ifFalse:
[aWorld runStepMethods.
Transcript flushDeferredEntry. "this is printing for #forceUpdate =
false"
self displayWorldSafely: aWorld].


For #forceUpdate = true the endEntry mechanism remains unchanged and failing
as before...



--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Eliot Miranda-2
In reply to this post by marcel.taeumel
Hi Marcel, Hi All,

On Mon, Jan 25, 2021 at 3:26 AM Marcel Taeumel <[hidden email]> wrote:
It's simply not thread-safe.

Hmm... in particular, there is a side effect (i.e. layout update) in the window's pluggable text morph. Even if #forceUpdate is false.

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.

That gave me a scare.  At first I thought that the effectiveProcess changes to ProcessorScheduler>>activeProcess could have made that expression no longer thread safe.  But it isn't anyway, and the above is very bad style.  Let me explain.

The suspension points in the VM are 
- the first bytecode of any non-quick method (following a primitive if it has one)
- the first bytecode of any block
- a backward branch

In the JIT there are fewer suspension points:
- the first bytecode of any method containing a send other than #== and/or #class (and given that ifNil:ifNotNil: is inlined into a send of #== and jumps, ifNil: sends don't count)
- the first bytecode of any block containing a send other than #== and/or #class
- a backward branch

So given

ProcessorScheduler>>#activeProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^activeProcess effectiveProcess

Process>>#effectiveProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^effectiveProcess ifNil: [self]

Project class>>#current
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^CurrentProject

MVCProject>>#uiProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^ world activeControllerProcess

MorphicProject>>#uiProcess
        "no suspension point here on any VM obeying the blue-book spec"
^uiProcess

we can see that "Processor activeProcess == Project current uiProcess" is not thread-safe in the interpreter; there is a suspension point in Project current before CurrentProject is returned (but not in the JIT); there is a suspension point in MVC in uiProcess.  There is a suspension point in ProcessorScheduler>>#activeProcess before the send of effectiveProcess to activeProcess, but since no data is fetched until the process resumes this is harmless.  However, were the expression written as "Project current uiProcess == Processor activeProcess" then this would no longer be harmless.

So on all VMs this is not reliably thread-safe.  Even on the JIT VM the first use of any method (except a doit) is interpreted.  Once the JIT VM, once it has started up and the methods have been jitted, "Processor activeProcess == Project current uiProcess" is thread-safe on Morphic, but not on MVC.  So we really shouldn't be using things like this.  They'll appear to work and in very rare circumstances they won't.  So let's please use the proper construct, Mutex.


But that makes me realize that the effectiveProcess changes are unsafe on an interpreter VM (and hence we're on;y getting away with it on the JIT VM).  Se really need some way of saying that ProcessorScheduler>>#activeProcess is atomic, e.g.

Process>>#effectiveProcess
        <noContextSwitch>
^effectiveProcess ifNil: [self]

But I don't know how to implement this right now.  Note that we do have 

BlockClosure>>#valueNoContextSwitch
"An exact copy of BlockClosure>>value except that this version will not preempt
the current process on block activation if a higher-priority process is runnable.
Primitive. Essential."
<primitive: 221>
numArgs ~= 0 ifTrue:
[self numArgsError: 0].
self primitiveFailed

FullBlockClosure>>#valueNoContextSwitch
"An exact copy of BlockClosure>>value except that this version will not preempt
the current process on block activation if a higher-priority process is runnable.
Primitive. Essential."
<primitive: 209>
numArgs ~= 0 ifTrue:
[self numArgsError: 0].
self primitiveFailed

but the VM uses the primitive numbers to control context switching.  We don't have a mechanism the VM can use to label a normal method such as Process>>#effectiveProcess as not to allow an interrupt.


Best,
Marcel

Am 23.01.2021 21:05:05 schrieb Levente Uzonyi <[hidden email]>:

On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.




--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Eliot Miranda-2


On Tue, Jan 26, 2021 at 2:45 PM Eliot Miranda <[hidden email]> wrote:
Hi Marcel, Hi All,

On Mon, Jan 25, 2021 at 3:26 AM Marcel Taeumel <[hidden email]> wrote:
It's simply not thread-safe.

Hmm... in particular, there is a side effect (i.e. layout update) in the window's pluggable text morph. Even if #forceUpdate is false.

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.

That gave me a scare.  At first I thought that the effectiveProcess changes to ProcessorScheduler>>activeProcess could have made that expression no longer thread safe.  But it isn't anyway, and the above is very bad style.  Let me explain.

The suspension points in the VM are 
- the first bytecode of any non-quick method (following a primitive if it has one)
- the first bytecode of any block
- a backward branch

In the JIT there are fewer suspension points:
- the first bytecode of any method containing a send other than #== and/or #class (and given that ifNil:ifNotNil: is inlined into a send of #== and jumps, ifNil: sends don't count)
- the first bytecode of any block containing a send other than #== and/or #class
- a backward branch

So given

ProcessorScheduler>>#activeProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^activeProcess effectiveProcess

Process>>#effectiveProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^effectiveProcess ifNil: [self]

Project class>>#current
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^CurrentProject

MVCProject>>#uiProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^ world activeControllerProcess

MorphicProject>>#uiProcess
        "no suspension point here on any VM obeying the blue-book spec"
^uiProcess

we can see that "Processor activeProcess == Project current uiProcess" is not thread-safe in the interpreter; there is a suspension point in Project current before CurrentProject is returned (but not in the JIT); there is a suspension point in MVC in uiProcess.  There is a suspension point in ProcessorScheduler>>#activeProcess before the send of effectiveProcess to activeProcess, but since no data is fetched until the process resumes this is harmless.  However, were the expression written as "Project current uiProcess == Processor activeProcess" then this would no longer be harmless.

So on all VMs this is not reliably thread-safe.  Even on the JIT VM the first use of any method (except a doit) is interpreted.  Once the JIT VM, once it has started up and the methods have been jitted, "Processor activeProcess == Project current uiProcess" is thread-safe on Morphic, but not on MVC.  So we really shouldn't be using things like this.  They'll appear to work and in very rare circumstances they won't.  So let's please use the proper construct, Mutex.


But that makes me realize that the effectiveProcess changes are unsafe on an interpreter VM (and hence we're on;y getting away with it on the JIT VM).  Se really need some way of saying that ProcessorScheduler>>#activeProcess is atomic, e.g.

Process>>#effectiveProcess
        <noContextSwitch>
^effectiveProcess ifNil: [self]

But I don't know how to implement this right now.  Note that we do have 

BlockClosure>>#valueNoContextSwitch
"An exact copy of BlockClosure>>value except that this version will not preempt
the current process on block activation if a higher-priority process is runnable.
Primitive. Essential."
<primitive: 221>
numArgs ~= 0 ifTrue:
[self numArgsError: 0].
self primitiveFailed

FullBlockClosure>>#valueNoContextSwitch
"An exact copy of BlockClosure>>value except that this version will not preempt
the current process on block activation if a higher-priority process is runnable.
Primitive. Essential."
<primitive: 209>
numArgs ~= 0 ifTrue:
[self numArgsError: 0].
self primitiveFailed

but the VM uses the primitive numbers to control context switching.  We don't have a mechanism the VM can use to label a normal method such as Process>>#effectiveProcess as not to allow an interrupt.

Doh!  We have primitive numbers.  We already use primitive 19 as a "simulation guard" in the debugger machinery as a "simulation guard" to avoid I'm not sure what.  Any unused numeric primitive could be used as a marker to avoid context switches.  I propose that we use it, hiding it behind a suitable pragma for syntactic sugar (i.e. <noContextSwitch>).  123 used to be primitiveValueUninteruptibly, which we no longer use.  I propose we use this.

Best,
Marcel

Am 23.01.2021 21:05:05 schrieb Levente Uzonyi <[hidden email]>:

On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.




--
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
Hi Eliot.

Thanks for the clarification of VM suspension points.

So let's please use the proper construct, Mutex.

Hmmm. I suppose it's not that easy. If #endEntry is running inside the ui process, we must not wait to not block the ui process (given that we want to implement #forceUpdate) -- but instead complete all deferred messages or just trigger a drawing cycle. A simple mutex wouldn't do because we want to avoid context switching altogether while executing methods that use such process comparision.

Thread-safety might be an issue in several places, which kind of use "Processor activeProcess == Project current uiProcess":

MorphicProject >> #spawnNewProcessIfThisIsUI:
(Morphic|MVC)Debugger class >> #openOn:...

Even there, a mutex would not do because we want to avoid comparing the wrong objects, not to avoid reentering that section. The entire expression "Processor activeProcess == Project current uiProcess" should be atomic.

Any unused numeric primitive could be used as a marker to avoid context switches.  I propose that we use it, hiding it behind a suitable pragma for syntactic sugar (i.e. <noContextSwitch>).  123 used to be primitiveValueUninteruptibly, which we no longer use.  I propose we use this.

+1 :-)

Best,
Marcel

Am 27.01.2021 01:35:09 schrieb Eliot Miranda <[hidden email]>:



On Tue, Jan 26, 2021 at 2:45 PM Eliot Miranda <[hidden email]> wrote:
Hi Marcel, Hi All,

On Mon, Jan 25, 2021 at 3:26 AM Marcel Taeumel <[hidden email]> wrote:
It's simply not thread-safe.

Hmm... in particular, there is a side effect (i.e. layout update) in the window's pluggable text morph. Even if #forceUpdate is false.

In #endEntry, we could check for "Processor activeProcess == Project current uiProcess" to then use "Project current addDeferredUIMessage..." automatically.

That gave me a scare.  At first I thought that the effectiveProcess changes to ProcessorScheduler>>activeProcess could have made that expression no longer thread safe.  But it isn't anyway, and the above is very bad style.  Let me explain.

The suspension points in the VM are 
- the first bytecode of any non-quick method (following a primitive if it has one)
- the first bytecode of any block
- a backward branch

In the JIT there are fewer suspension points:
- the first bytecode of any method containing a send other than #== and/or #class (and given that ifNil:ifNotNil: is inlined into a send of #== and jumps, ifNil: sends don't count)
- the first bytecode of any block containing a send other than #== and/or #class
- a backward branch

So given

ProcessorScheduler>>#activeProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^activeProcess effectiveProcess

Process>>#effectiveProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^effectiveProcess ifNil: [self]

Project class>>#current
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^CurrentProject

MVCProject>>#uiProcess
        "suspension point here on an interpreter VM, and the first time in the JIT VM"
^ world activeControllerProcess

MorphicProject>>#uiProcess
        "no suspension point here on any VM obeying the blue-book spec"
^uiProcess

we can see that "Processor activeProcess == Project current uiProcess" is not thread-safe in the interpreter; there is a suspension point in Project current before CurrentProject is returned (but not in the JIT); there is a suspension point in MVC in uiProcess.  There is a suspension point in ProcessorScheduler>>#activeProcess before the send of effectiveProcess to activeProcess, but since no data is fetched until the process resumes this is harmless.  However, were the expression written as "Project current uiProcess == Processor activeProcess" then this would no longer be harmless.

So on all VMs this is not reliably thread-safe.  Even on the JIT VM the first use of any method (except a doit) is interpreted.  Once the JIT VM, once it has started up and the methods have been jitted, "Processor activeProcess == Project current uiProcess" is thread-safe on Morphic, but not on MVC.  So we really shouldn't be using things like this.  They'll appear to work and in very rare circumstances they won't.  So let's please use the proper construct, Mutex.


But that makes me realize that the effectiveProcess changes are unsafe on an interpreter VM (and hence we're on;y getting away with it on the JIT VM).  Se really need some way of saying that ProcessorScheduler>>#activeProcess is atomic, e.g.

Process>>#effectiveProcess
        <noContextSwitch>
^effectiveProcess ifNil: [self]

But I don't know how to implement this right now.  Note that we do have 

BlockClosure>>#valueNoContextSwitch
"An exact copy of BlockClosure>>value except that this version will not preempt
the current process on block activation if a higher-priority process is runnable.
Primitive. Essential."
<primitive: 221>
numArgs ~= 0 ifTrue:
[self numArgsError: 0].
self primitiveFailed

FullBlockClosure>>#valueNoContextSwitch
"An exact copy of BlockClosure>>value except that this version will not preempt
the current process on block activation if a higher-priority process is runnable.
Primitive. Essential."
<primitive: 209>
numArgs ~= 0 ifTrue:
[self numArgsError: 0].
self primitiveFailed

but the VM uses the primitive numbers to control context switching.  We don't have a mechanism the VM can use to label a normal method such as Process>>#effectiveProcess as not to allow an interrupt.

Doh!  We have primitive numbers.  We already use primitive 19 as a "simulation guard" in the debugger machinery as a "simulation guard" to avoid I'm not sure what.  Any unused numeric primitive could be used as a marker to avoid context switches.  I propose that we use it, hiding it behind a suitable pragma for syntactic sugar (i.e. <noContextSwitch>).  123 used to be primitiveValueUninteruptibly, which we no longer use.  I propose we use this.

Best,
Marcel

Am 23.01.2021 21:05:05 schrieb Levente Uzonyi <[hidden email]>:

On Fri, 22 Jan 2021, tim Rowledge wrote:

>
>
>> On 2021-01-22, at 5:26 PM, David T. Lewis wrote:
>>
>> On Fri, Jan 22, 2021 at 02:03:10PM -0600, jaromir wrote:
>>> I ran the following in Squeak 5.3:
>>>
>>> TranscriptStream forceUpdate: false.
>>> [ 10000 timesRepeat: [ Transcript show: 'x' ] ] forkAt: 39
>>>
>>> When the Transcript window fills up, 'Message not understood' and 'Assertion
>>> failure' appear. I'm wondering is this a bug? Thanks. Jaromir
>>>
>>>
>>>
>>
>> It certainly does appear to be a bug.
>
> We had some discussion about it not so long ago; you can't use the Transcript to log bugs from Seaside very well, for example. Background processes, mutexs, that sort of thing. IIRC Levente offered an explanation?

It's simply not thread-safe.
If you want it to be thread-safe, you can pass your message to the UI
process to show it. Make sure your message is computed in its own process
and is passed as a precomputed string to avoid other kinds of race
conditions. E.g.:

| theMessage |
theMessage := 'Something very {1}.' format: { 'important' }.
Project current addDeferredUIMessage: [ Transcript show: theMessage; cr ].


Levente

>
>
> tim
> --
> tim Rowledge; [hidden email]; http://www.rowledge.org/tim
> The hardness of the butter is proportional to the softness of the bread.




--
_,,,^..^,,,_
best, Eliot


--
_,,,^..^,,,_
best, Eliot


Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Levente Uzonyi
In reply to this post by marcel.taeumel
Hi Marcel,

On Tue, 26 Jan 2021, Marcel Taeumel wrote:

> Hi Jaromir,
> please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>
> @all: Would Transcript now be thread-safe? Did I miss something?

Not at all. The problem is not #endEntry but all the other
methods writing to the stream e.g.: #nextPutAll:.
The real solution is to replace the stream-like API with one that provides
an easy way to write messages in a thread-safe way. E.g.:

Transcript showStream: [ :stream |
  stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]

Which guarantees that 'Hello World!' appears on the Transcript without
being mixed with other messages.

or

Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.

The implementations could be e.g.:

showStream: aBlock

  | string |
  string := String streamContents: aBlock.
  self safelyNextPutAll: string

show: aString format: arguments

  | string |
  string := aString format: arguments.
  self safelyNextPutAll: string

The last challenge is to implement #safelyNextPutAll: which involves
making Transcript not be a TranscriptStream.
Transcript should encapsulate the stream and use a Mutex (not a global one
because its pointless) to provide thread-safety while writing the
characters on it or reading its contents. E.g.:

safelyNextPutAll: aString

  mutex critical: [
  stream nextPutAll: aString ]

For backwards compatibility, the stream-API methods must be provided for a
while but those methods should be thread-safe on their own. E.g.:

nextPutAll: aString

  self safelyNextPutAll: aString

nextPut: aCharacter

  self safelyNextPutAll: aCharacter asString


Levente

>
> Best,
> Marcel
>
>       Am 25.01.2021 21:38:36 schrieb jaromir <[hidden email]>:
>
>       Well, I tried deferring the whole Transcript endEntry machinery to the UI
>       doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
>       #forceUpdate = false only ... and it seems to avoid the problem!
>
>       TranscriptStream >> endEntry
>
>       deferredEntry ifNil: [ false ]. "this is a new instance variable"
>       self semaphore critical:[
>       self class forceUpdate
>       ifTrue: [self changed: #appendEntry; reset]
>       ifFalse: [deferredEntry := true].
>
>
>       TranscriptStream >> flushDeferredEntry
>       "This is run every UI cycle in doOneCycleNowFor:"
>
>       deferredEntry ifTrue: [
>       self class forceUpdate: true.
>       self endEntry.
>       deferredEntry := false.
>       self class forceUpdate: false.
>       ]
>
>       doOneCycleNowFor: aWorld
>
>       "... the whole body remains unchanged except:"
>
>       capturingGesture ifFalse:
>       [aWorld runStepMethods.
>       Transcript flushDeferredEntry. "this is printing for #forceUpdate =
>       false"
>       self displayWorldSafely: aWorld].
>
>
>       For #forceUpdate = true the endEntry mechanism remains unchanged and failing
>       as before...
>
>
>
>       --
>       Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
Hi Levente.

Thanks für clarification. We are talking about more than one problem here. :-) I did just address the issue with errors/debuggers happening due to UI process disruptions. You are also concerned about the order of contents in the Transcript's contents. Both are very important and required for claiming that it would be thread-safe.

(Which reminds me that I find it very strange that the UI components (e.g. PluggableTextMorph) do also check the Transcript's internal characterLimit. I would expect an append-only behavior when a Transcript window is open.)

Anyway, your proposal about a safelyNextPut(All): looks good. :-) +1

Best,
Marcel

Am 27.01.2021 23:05:21 schrieb Levente Uzonyi <[hidden email]>:

Hi Marcel,

On Tue, 26 Jan 2021, Marcel Taeumel wrote:

> Hi Jaromir,
> please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>
> @all: Would Transcript now be thread-safe? Did I miss something?

Not at all. The problem is not #endEntry but all the other
methods writing to the stream e.g.: #nextPutAll:.
The real solution is to replace the stream-like API with one that provides
an easy way to write messages in a thread-safe way. E.g.:

Transcript showStream: [ :stream |
stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]

Which guarantees that 'Hello World!' appears on the Transcript without
being mixed with other messages.

or

Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.

The implementations could be e.g.:

showStream: aBlock

| string |
string := String streamContents: aBlock.
self safelyNextPutAll: string

show: aString format: arguments

| string |
string := aString format: arguments.
self safelyNextPutAll: string

The last challenge is to implement #safelyNextPutAll: which involves
making Transcript not be a TranscriptStream.
Transcript should encapsulate the stream and use a Mutex (not a global one
because its pointless) to provide thread-safety while writing the
characters on it or reading its contents. E.g.:

safelyNextPutAll: aString

mutex critical: [
stream nextPutAll: aString ]

For backwards compatibility, the stream-API methods must be provided for a
while but those methods should be thread-safe on their own. E.g.:

nextPutAll: aString

self safelyNextPutAll: aString

nextPut: aCharacter

self safelyNextPutAll: aCharacter asString


Levente

>
> Best,
> Marcel
>
> Am 25.01.2021 21:38:36 schrieb jaromir :
>
> Well, I tried deferring the whole Transcript endEntry machinery to the UI
> doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
> #forceUpdate = false only ... and it seems to avoid the problem!
>
> TranscriptStream >> endEntry
>
> deferredEntry ifNil: [ false ]. "this is a new instance variable"
> self semaphore critical:[
> self class forceUpdate
> ifTrue: [self changed: #appendEntry; reset]
> ifFalse: [deferredEntry := true].
>
>
> TranscriptStream >> flushDeferredEntry
> "This is run every UI cycle in doOneCycleNowFor:"
>
> deferredEntry ifTrue: [
> self class forceUpdate: true.
> self endEntry.
> deferredEntry := false.
> self class forceUpdate: false.
> ]
>
> doOneCycleNowFor: aWorld
>
> "... the whole body remains unchanged except:"
>
> capturingGesture ifFalse:
> [aWorld runStepMethods.
> Transcript flushDeferredEntry. "this is printing for #forceUpdate =
> false"
> self displayWorldSafely: aWorld].
>
>
> For #forceUpdate = true the endEntry mechanism remains unchanged and failing
> as before...
>
>
>
> --
> Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>
>
>



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Tobias Pape
Hi

apart from the debuggers, why bother making Transcript thread-safe in the first place?

The Transcript is a mere output stream for debugging/logging etc., our stdout or stderr.

A separate kind of log (that optionally could output to the transcript) would be more apt IMHO.
Thread safety could be addressed there without complicating Transcript more…

my 2ct
        -tobias


> On 28. Jan 2021, at 09:05, Marcel Taeumel <[hidden email]> wrote:
>
> Hi Levente.
>
> Thanks für clarification. We are talking about more than one problem here. :-) I did just address the issue with errors/debuggers happening due to UI process disruptions. You are also concerned about the order of contents in the Transcript's contents. Both are very important and required for claiming that it would be thread-safe.
>
> (Which reminds me that I find it very strange that the UI components (e.g. PluggableTextMorph) do also check the Transcript's internal characterLimit. I would expect an append-only behavior when a Transcript window is open.)
>
> Anyway, your proposal about a safelyNextPut(All): looks good. :-) +1
>
> Best,
> Marcel
>> Am 27.01.2021 23:05:21 schrieb Levente Uzonyi <[hidden email]>:
>>
>> Hi Marcel,
>>
>> On Tue, 26 Jan 2021, Marcel Taeumel wrote:
>>
>> > Hi Jaromir,
>> > please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>> >
>> > @all: Would Transcript now be thread-safe? Did I miss something?
>>
>> Not at all. The problem is not #endEntry but all the other
>> methods writing to the stream e.g.: #nextPutAll:.
>> The real solution is to replace the stream-like API with one that provides
>> an easy way to write messages in a thread-safe way. E.g.:
>>
>> Transcript showStream: [ :stream |
>> stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]
>>
>> Which guarantees that 'Hello World!' appears on the Transcript without
>> being mixed with other messages.
>>
>> or
>>
>> Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.
>>
>> The implementations could be e.g.:
>>
>> showStream: aBlock
>>
>> | string |
>> string := String streamContents: aBlock.
>> self safelyNextPutAll: string
>>
>> show: aString format: arguments
>>
>> | string |
>> string := aString format: arguments.
>> self safelyNextPutAll: string
>>
>> The last challenge is to implement #safelyNextPutAll: which involves
>> making Transcript not be a TranscriptStream.
>> Transcript should encapsulate the stream and use a Mutex (not a global one
>> because its pointless) to provide thread-safety while writing the
>> characters on it or reading its contents. E.g.:
>>
>> safelyNextPutAll: aString
>>
>> mutex critical: [
>> stream nextPutAll: aString ]
>>
>> For backwards compatibility, the stream-API methods must be provided for a
>> while but those methods should be thread-safe on their own. E.g.:
>>
>> nextPutAll: aString
>>
>> self safelyNextPutAll: aString
>>
>> nextPut: aCharacter
>>
>> self safelyNextPutAll: aCharacter asString
>>
>>
>> Levente
>>
>> >
>> > Best,
>> > Marcel
>> >
>> > Am 25.01.2021 21:38:36 schrieb jaromir :
>> >
>> > Well, I tried deferring the whole Transcript endEntry machinery to the UI
>> > doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
>> > #forceUpdate = false only ... and it seems to avoid the problem!
>> >
>> > TranscriptStream >> endEntry
>> >
>> > deferredEntry ifNil: [ false ]. "this is a new instance variable"
>> > self semaphore critical:[
>> > self class forceUpdate
>> > ifTrue: [self changed: #appendEntry; reset]
>> > ifFalse: [deferredEntry := true].
>> >
>> >
>> > TranscriptStream >> flushDeferredEntry
>> > "This is run every UI cycle in doOneCycleNowFor:"
>> >
>> > deferredEntry ifTrue: [
>> > self class forceUpdate: true.
>> > self endEntry.
>> > deferredEntry := false.
>> > self class forceUpdate: false.
>> > ]
>> >
>> > doOneCycleNowFor: aWorld
>> >
>> > "... the whole body remains unchanged except:"
>> >
>> > capturingGesture ifFalse:
>> > [aWorld runStepMethods.
>> > Transcript flushDeferredEntry. "this is printing for #forceUpdate =
>> > false"
>> > self displayWorldSafely: aWorld].
>> >
>> >
>> > For #forceUpdate = true the endEntry mechanism remains unchanged and failing
>> > as before...
>> >
>> >
>> >
>> > --
>> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>> >
>> >
>> >
>>
>



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
Hi Tobias

A separate kind of log (that optionally could output to the transcript)

Transcript can use stdout already. Not sure we want to shift the problem to the user again ... :-/ "Every time you provide an option, you expect users to make a decision." :-)

Best,
Marcel

Am 28.01.2021 10:32:27 schrieb Tobias Pape <[hidden email]>:

Hi

apart from the debuggers, why bother making Transcript thread-safe in the first place?

The Transcript is a mere output stream for debugging/logging etc., our stdout or stderr.

A separate kind of log (that optionally could output to the transcript) would be more apt IMHO.
Thread safety could be addressed there without complicating Transcript more…

my 2ct
-tobias


> On 28. Jan 2021, at 09:05, Marcel Taeumel wrote:
>
> Hi Levente.
>
> Thanks für clarification. We are talking about more than one problem here. :-) I did just address the issue with errors/debuggers happening due to UI process disruptions. You are also concerned about the order of contents in the Transcript's contents. Both are very important and required for claiming that it would be thread-safe.
>
> (Which reminds me that I find it very strange that the UI components (e.g. PluggableTextMorph) do also check the Transcript's internal characterLimit. I would expect an append-only behavior when a Transcript window is open.)
>
> Anyway, your proposal about a safelyNextPut(All): looks good. :-) +1
>
> Best,
> Marcel
>> Am 27.01.2021 23:05:21 schrieb Levente Uzonyi :
>>
>> Hi Marcel,
>>
>> On Tue, 26 Jan 2021, Marcel Taeumel wrote:
>>
>> > Hi Jaromir,
>> > please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>> >
>> > @all: Would Transcript now be thread-safe? Did I miss something?
>>
>> Not at all. The problem is not #endEntry but all the other
>> methods writing to the stream e.g.: #nextPutAll:.
>> The real solution is to replace the stream-like API with one that provides
>> an easy way to write messages in a thread-safe way. E.g.:
>>
>> Transcript showStream: [ :stream |
>> stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]
>>
>> Which guarantees that 'Hello World!' appears on the Transcript without
>> being mixed with other messages.
>>
>> or
>>
>> Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.
>>
>> The implementations could be e.g.:
>>
>> showStream: aBlock
>>
>> | string |
>> string := String streamContents: aBlock.
>> self safelyNextPutAll: string
>>
>> show: aString format: arguments
>>
>> | string |
>> string := aString format: arguments.
>> self safelyNextPutAll: string
>>
>> The last challenge is to implement #safelyNextPutAll: which involves
>> making Transcript not be a TranscriptStream.
>> Transcript should encapsulate the stream and use a Mutex (not a global one
>> because its pointless) to provide thread-safety while writing the
>> characters on it or reading its contents. E.g.:
>>
>> safelyNextPutAll: aString
>>
>> mutex critical: [
>> stream nextPutAll: aString ]
>>
>> For backwards compatibility, the stream-API methods must be provided for a
>> while but those methods should be thread-safe on their own. E.g.:
>>
>> nextPutAll: aString
>>
>> self safelyNextPutAll: aString
>>
>> nextPut: aCharacter
>>
>> self safelyNextPutAll: aCharacter asString
>>
>>
>> Levente
>>
>> >
>> > Best,
>> > Marcel
>> >
>> > Am 25.01.2021 21:38:36 schrieb jaromir :
>> >
>> > Well, I tried deferring the whole Transcript endEntry machinery to the UI
>> > doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
>> > #forceUpdate = false only ... and it seems to avoid the problem!
>> >
>> > TranscriptStream >> endEntry
>> >
>> > deferredEntry ifNil: [ false ]. "this is a new instance variable"
>> > self semaphore critical:[
>> > self class forceUpdate
>> > ifTrue: [self changed: #appendEntry; reset]
>> > ifFalse: [deferredEntry := true].
>> >
>> >
>> > TranscriptStream >> flushDeferredEntry
>> > "This is run every UI cycle in doOneCycleNowFor:"
>> >
>> > deferredEntry ifTrue: [
>> > self class forceUpdate: true.
>> > self endEntry.
>> > deferredEntry := false.
>> > self class forceUpdate: false.
>> > ]
>> >
>> > doOneCycleNowFor: aWorld
>> >
>> > "... the whole body remains unchanged except:"
>> >
>> > capturingGesture ifFalse:
>> > [aWorld runStepMethods.
>> > Transcript flushDeferredEntry. "this is printing for #forceUpdate =
>> > false"
>> > self displayWorldSafely: aWorld].
>> >
>> >
>> > For #forceUpdate = true the endEntry mechanism remains unchanged and failing
>> > as before...
>> >
>> >
>> >
>> > --
>> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>> >
>> >
>> >
>>
>





Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Tobias Pape


> On 28. Jan 2021, at 10:41, Marcel Taeumel <[hidden email]> wrote:
>
> Hi Tobias
>
> > A separate kind of log (that optionally could output to the transcript)
>
> Transcript can use stdout already. Not sure we want to shift the problem to the user again ... :-/ "Every time you provide an option, you expect users to make a decision." :-)
>

That's not what I meant.

Transcript is Smalltalk for stdout, I would say.
Complicating it to act like a proper logger is IMHO ill-directed.

Neither C printf nor Java System.out.println[1] nor (i think) Racket display are thread safe.

And I think this is ok.

Transcript shouldn't break when used concurrently, but I don't think it should do something sophisticated…

Transcript is not a good logger. We shouldn't pretend it is.

Maybe GemStone's object log is a neat abstraction. Thread-safe, object based, with time stamps.



Best regards
        -Tobias

[1]: https://stackoverflow.com/questions/32880557/is-system-out-println-thread-safe-by-default

> Best,
> Marcel
>> Am 28.01.2021 10:32:27 schrieb Tobias Pape <[hidden email]>:
>>
>> Hi
>>
>> apart from the debuggers, why bother making Transcript thread-safe in the first place?
>>
>> The Transcript is a mere output stream for debugging/logging etc., our stdout or stderr.
>>
>> A separate kind of log (that optionally could output to the transcript) would be more apt IMHO.
>> Thread safety could be addressed there without complicating Transcript more…
>>
>> my 2ct
>> -tobias
>>
>>
>> > On 28. Jan 2021, at 09:05, Marcel Taeumel wrote:
>> >
>> > Hi Levente.
>> >
>> > Thanks für clarification. We are talking about more than one problem here. :-) I did just address the issue with errors/debuggers happening due to UI process disruptions. You are also concerned about the order of contents in the Transcript's contents. Both are very important and required for claiming that it would be thread-safe.
>> >
>> > (Which reminds me that I find it very strange that the UI components (e.g. PluggableTextMorph) do also check the Transcript's internal characterLimit. I would expect an append-only behavior when a Transcript window is open.)
>> >
>> > Anyway, your proposal about a safelyNextPut(All): looks good. :-) +1
>> >
>> > Best,
>> > Marcel
>> >> Am 27.01.2021 23:05:21 schrieb Levente Uzonyi :
>> >>
>> >> Hi Marcel,
>> >>
>> >> On Tue, 26 Jan 2021, Marcel Taeumel wrote:
>> >>
>> >> > Hi Jaromir,
>> >> > please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>> >> >
>> >> > @all: Would Transcript now be thread-safe? Did I miss something?
>> >>
>> >> Not at all. The problem is not #endEntry but all the other
>> >> methods writing to the stream e.g.: #nextPutAll:.
>> >> The real solution is to replace the stream-like API with one that provides
>> >> an easy way to write messages in a thread-safe way. E.g.:
>> >>
>> >> Transcript showStream: [ :stream |
>> >> stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]
>> >>
>> >> Which guarantees that 'Hello World!' appears on the Transcript without
>> >> being mixed with other messages.
>> >>
>> >> or
>> >>
>> >> Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.
>> >>
>> >> The implementations could be e.g.:
>> >>
>> >> showStream: aBlock
>> >>
>> >> | string |
>> >> string := String streamContents: aBlock.
>> >> self safelyNextPutAll: string
>> >>
>> >> show: aString format: arguments
>> >>
>> >> | string |
>> >> string := aString format: arguments.
>> >> self safelyNextPutAll: string
>> >>
>> >> The last challenge is to implement #safelyNextPutAll: which involves
>> >> making Transcript not be a TranscriptStream.
>> >> Transcript should encapsulate the stream and use a Mutex (not a global one
>> >> because its pointless) to provide thread-safety while writing the
>> >> characters on it or reading its contents. E.g.:
>> >>
>> >> safelyNextPutAll: aString
>> >>
>> >> mutex critical: [
>> >> stream nextPutAll: aString ]
>> >>
>> >> For backwards compatibility, the stream-API methods must be provided for a
>> >> while but those methods should be thread-safe on their own. E.g.:
>> >>
>> >> nextPutAll: aString
>> >>
>> >> self safelyNextPutAll: aString
>> >>
>> >> nextPut: aCharacter
>> >>
>> >> self safelyNextPutAll: aCharacter asString
>> >>
>> >>
>> >> Levente
>> >>
>> >> >
>> >> > Best,
>> >> > Marcel
>> >> >
>> >> > Am 25.01.2021 21:38:36 schrieb jaromir :
>> >> >
>> >> > Well, I tried deferring the whole Transcript endEntry machinery to the UI
>> >> > doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
>> >> > #forceUpdate = false only ... and it seems to avoid the problem!
>> >> >
>> >> > TranscriptStream >> endEntry
>> >> >
>> >> > deferredEntry ifNil: [ false ]. "this is a new instance variable"
>> >> > self semaphore critical:[
>> >> > self class forceUpdate
>> >> > ifTrue: [self changed: #appendEntry; reset]
>> >> > ifFalse: [deferredEntry := true].
>> >> >
>> >> >
>> >> > TranscriptStream >> flushDeferredEntry
>> >> > "This is run every UI cycle in doOneCycleNowFor:"
>> >> >
>> >> > deferredEntry ifTrue: [
>> >> > self class forceUpdate: true.
>> >> > self endEntry.
>> >> > deferredEntry := false.
>> >> > self class forceUpdate: false.
>> >> > ]
>> >> >
>> >> > doOneCycleNowFor: aWorld
>> >> >
>> >> > "... the whole body remains unchanged except:"
>> >> >
>> >> > capturingGesture ifFalse:
>> >> > [aWorld runStepMethods.
>> >> > Transcript flushDeferredEntry. "this is printing for #forceUpdate =
>> >> > false"
>> >> > self displayWorldSafely: aWorld].
>> >> >
>> >> >
>> >> > For #forceUpdate = true the endEntry mechanism remains unchanged and failing
>> >> > as before...
>> >> >
>> >> >
>> >> >
>> >> > --
>> >> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>> >> >
>> >> >
>> >> >
>> >>
>> >
>>
>>
>>
>



Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

Christoph Thiede
In reply to this post by marcel.taeumel

Hi all,


this is an interesting discussion, two thoughts from my side:


1. What is the problem with "Processor activeProcess == Project current uiProcess" not being a thread-safe expression? I see that it could be possible at many points that the VM interrupts the current process and resumes another one first; but still, activeProcess will always hold the same Process instance when the same process is active, independently of any interruptions, won't it? What is that dangerous thing that could happen during a suspension point in the VM? What am I missing here? :-)


2. To Levente's proposal of making Transcript calls thread-safe:


safelyNextPutAll: aString

>          mutex critical: [
>                  stream nextPutAll: aString ]

So this would delay the execution of any process that uses the Transcript, wouldn't it? Hm ... I would rather have expected the Transcript calls to be deferred when the Transcript is locked ATM - *unless* #forceUpdate is enabled, maybe. Let's keep everyday Transcript clients blocking-free whenever possible. Or imagine several processes writing to the Transcript, but the transcript not being opened at this time. There should no need to block any of the calling processes?

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Donnerstag, 28. Januar 2021 10:41:03
An: squeak-dev
Betreff: Re: [squeak-dev] Transcript error when forceUpdate: false (?)
 
Hi Tobias

A separate kind of log (that optionally could output to the transcript)

Transcript can use stdout already. Not sure we want to shift the problem to the user again ... :-/ "Every time you provide an option, you expect users to make a decision." :-)

Best,
Marcel

Am 28.01.2021 10:32:27 schrieb Tobias Pape <[hidden email]>:

Hi

apart from the debuggers, why bother making Transcript thread-safe in the first place?

The Transcript is a mere output stream for debugging/logging etc., our stdout or stderr.

A separate kind of log (that optionally could output to the transcript) would be more apt IMHO.
Thread safety could be addressed there without complicating Transcript more…

my 2ct
-tobias


> On 28. Jan 2021, at 09:05, Marcel Taeumel wrote:
>
> Hi Levente.
>
> Thanks für clarification. We are talking about more than one problem here. :-) I did just address the issue with errors/debuggers happening due to UI process disruptions. You are also concerned about the order of contents in the Transcript's contents. Both are very important and required for claiming that it would be thread-safe.
>
> (Which reminds me that I find it very strange that the UI components (e.g. PluggableTextMorph) do also check the Transcript's internal characterLimit. I would expect an append-only behavior when a Transcript window is open.)
>
> Anyway, your proposal about a safelyNextPut(All): looks good. :-) +1
>
> Best,
> Marcel
>> Am 27.01.2021 23:05:21 schrieb Levente Uzonyi :
>>
>> Hi Marcel,
>>
>> On Tue, 26 Jan 2021, Marcel Taeumel wrote:
>>
>> > Hi Jaromir,
>> > please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>> >
>> > @all: Would Transcript now be thread-safe? Did I miss something?
>>
>> Not at all. The problem is not #endEntry but all the other
>> methods writing to the stream e.g.: #nextPutAll:.
>> The real solution is to replace the stream-like API with one that provides
>> an easy way to write messages in a thread-safe way. E.g.:
>>
>> Transcript showStream: [ :stream |
>> stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]
>>
>> Which guarantees that 'Hello World!' appears on the Transcript without
>> being mixed with other messages.
>>
>> or
>>
>> Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.
>>
>> The implementations could be e.g.:
>>
>> showStream: aBlock
>>
>> | string |
>> string := String streamContents: aBlock.
>> self safelyNextPutAll: string
>>
>> show: aString format: arguments
>>
>> | string |
>> string := aString format: arguments.
>> self safelyNextPutAll: string
>>
>> The last challenge is to implement #safelyNextPutAll: which involves
>> making Transcript not be a TranscriptStream.
>> Transcript should encapsulate the stream and use a Mutex (not a global one
>> because its pointless) to provide thread-safety while writing the
>> characters on it or reading its contents. E.g.:
>>
>> safelyNextPutAll: aString
>>
>> mutex critical: [
>> stream nextPutAll: aString ]
>>
>> For backwards compatibility, the stream-API methods must be provided for a
>> while but those methods should be thread-safe on their own. E.g.:
>>
>> nextPutAll: aString
>>
>> self safelyNextPutAll: aString
>>
>> nextPut: aCharacter
>>
>> self safelyNextPutAll: aCharacter asString
>>
>>
>> Levente
>>
>> >
>> > Best,
>> > Marcel
>> >
>> > Am 25.01.2021 21:38:36 schrieb jaromir :
>> >
>> > Well, I tried deferring the whole Transcript endEntry machinery to the UI
>> > doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
>> > #forceUpdate = false only ... and it seems to avoid the problem!
>> >
>> > TranscriptStream >> endEntry
>> >
>> > deferredEntry ifNil: [ false ]. "this is a new instance variable"
>> > self semaphore critical:[
>> > self class forceUpdate
>> > ifTrue: [self changed: #appendEntry; reset]
>> > ifFalse: [deferredEntry := true].
>> >
>> >
>> > TranscriptStream >> flushDeferredEntry
>> > "This is run every UI cycle in doOneCycleNowFor:"
>> >
>> > deferredEntry ifTrue: [
>> > self class forceUpdate: true.
>> > self endEntry.
>> > deferredEntry := false.
>> > self class forceUpdate: false.
>> > ]
>> >
>> > doOneCycleNowFor: aWorld
>> >
>> > "... the whole body remains unchanged except:"
>> >
>> > capturingGesture ifFalse:
>> > [aWorld runStepMethods.
>> > Transcript flushDeferredEntry. "this is printing for #forceUpdate =
>> > false"
>> > self displayWorldSafely: aWorld].
>> >
>> >
>> > For #forceUpdate = true the endEntry mechanism remains unchanged and failing
>> > as before...
>> >
>> >
>> >
>> > --
>> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>> >
>> >
>> >
>>
>





Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: Transcript error when forceUpdate: false (?)

marcel.taeumel
Hi Christoph.

 What is that dangerous thing that could happen during a suspension point in the VM? What am I missing here? :-)

It's only part of the problem. There could be a disconnect between the former "Project current uiProcess" comparison and how "addDeferredUIMessage" is processed later in the code. It would be better to no preempt this call as a whole.

Best,
Marcel

Am 28.01.2021 10:59:50 schrieb Thiede, Christoph <[hidden email]>:

Hi all,


this is an interesting discussion, two thoughts from my side:


1. What is the problem with "Processor activeProcess == Project current uiProcess" not being a thread-safe expression? I see that it could be possible at many points that the VM interrupts the current process and resumes another one first; but still, activeProcess will always hold the same Process instance when the same process is active, independently of any interruptions, won't it? What is that dangerous thing that could happen during a suspension point in the VM? What am I missing here? :-)


2. To Levente's proposal of making Transcript calls thread-safe:


safelyNextPutAll: aString

>          mutex critical: [
>                  stream nextPutAll: aString ]

So this would delay the execution of any process that uses the Transcript, wouldn't it? Hm ... I would rather have expected the Transcript calls to be deferred when the Transcript is locked ATM - *unless* #forceUpdate is enabled, maybe. Let's keep everyday Transcript clients blocking-free whenever possible. Or imagine several processes writing to the Transcript, but the transcript not being opened at this time. There should no need to block any of the calling processes?

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Donnerstag, 28. Januar 2021 10:41:03
An: squeak-dev
Betreff: Re: [squeak-dev] Transcript error when forceUpdate: false (?)
 
Hi Tobias

A separate kind of log (that optionally could output to the transcript)

Transcript can use stdout already. Not sure we want to shift the problem to the user again ... :-/ "Every time you provide an option, you expect users to make a decision." :-)

Best,
Marcel

Am 28.01.2021 10:32:27 schrieb Tobias Pape <[hidden email]>:

Hi

apart from the debuggers, why bother making Transcript thread-safe in the first place?

The Transcript is a mere output stream for debugging/logging etc., our stdout or stderr.

A separate kind of log (that optionally could output to the transcript) would be more apt IMHO.
Thread safety could be addressed there without complicating Transcript more…

my 2ct
-tobias


> On 28. Jan 2021, at 09:05, Marcel Taeumel wrote:
>
> Hi Levente.
>
> Thanks für clarification. We are talking about more than one problem here. :-) I did just address the issue with errors/debuggers happening due to UI process disruptions. You are also concerned about the order of contents in the Transcript's contents. Both are very important and required for claiming that it would be thread-safe.
>
> (Which reminds me that I find it very strange that the UI components (e.g. PluggableTextMorph) do also check the Transcript's internal characterLimit. I would expect an append-only behavior when a Transcript window is open.)
>
> Anyway, your proposal about a safelyNextPut(All): looks good. :-) +1
>
> Best,
> Marcel
>> Am 27.01.2021 23:05:21 schrieb Levente Uzonyi :
>>
>> Hi Marcel,
>>
>> On Tue, 26 Jan 2021, Marcel Taeumel wrote:
>>
>> > Hi Jaromir,
>> > please take a look at Collections-mt.923 (inbox). Maybe those changes would satisfy all your current needs in this regard. :-)
>> >
>> > @all: Would Transcript now be thread-safe? Did I miss something?
>>
>> Not at all. The problem is not #endEntry but all the other
>> methods writing to the stream e.g.: #nextPutAll:.
>> The real solution is to replace the stream-like API with one that provides
>> an easy way to write messages in a thread-safe way. E.g.:
>>
>> Transcript showStream: [ :stream |
>> stream nextPutAll: 'Hello'; space; nextPutAll: ' World!'; cr ]
>>
>> Which guarantees that 'Hello World!' appears on the Transcript without
>> being mixed with other messages.
>>
>> or
>>
>> Transcript show: 'Hello {1}!{2}' format: { 'World'. String cr }.
>>
>> The implementations could be e.g.:
>>
>> showStream: aBlock
>>
>> | string |
>> string := String streamContents: aBlock.
>> self safelyNextPutAll: string
>>
>> show: aString format: arguments
>>
>> | string |
>> string := aString format: arguments.
>> self safelyNextPutAll: string
>>
>> The last challenge is to implement #safelyNextPutAll: which involves
>> making Transcript not be a TranscriptStream.
>> Transcript should encapsulate the stream and use a Mutex (not a global one
>> because its pointless) to provide thread-safety while writing the
>> characters on it or reading its contents. E.g.:
>>
>> safelyNextPutAll: aString
>>
>> mutex critical: [
>> stream nextPutAll: aString ]
>>
>> For backwards compatibility, the stream-API methods must be provided for a
>> while but those methods should be thread-safe on their own. E.g.:
>>
>> nextPutAll: aString
>>
>> self safelyNextPutAll: aString
>>
>> nextPut: aCharacter
>>
>> self safelyNextPutAll: aCharacter asString
>>
>>
>> Levente
>>
>> >
>> > Best,
>> > Marcel
>> >
>> > Am 25.01.2021 21:38:36 schrieb jaromir :
>> >
>> > Well, I tried deferring the whole Transcript endEntry machinery to the UI
>> > doOneCycle (bypassing the changed: #appendEntryLater mechanism) for
>> > #forceUpdate = false only ... and it seems to avoid the problem!
>> >
>> > TranscriptStream >> endEntry
>> >
>> > deferredEntry ifNil: [ false ]. "this is a new instance variable"
>> > self semaphore critical:[
>> > self class forceUpdate
>> > ifTrue: [self changed: #appendEntry; reset]
>> > ifFalse: [deferredEntry := true].
>> >
>> >
>> > TranscriptStream >> flushDeferredEntry
>> > "This is run every UI cycle in doOneCycleNowFor:"
>> >
>> > deferredEntry ifTrue: [
>> > self class forceUpdate: true.
>> > self endEntry.
>> > deferredEntry := false.
>> > self class forceUpdate: false.
>> > ]
>> >
>> > doOneCycleNowFor: aWorld
>> >
>> > "... the whole body remains unchanged except:"
>> >
>> > capturingGesture ifFalse:
>> > [aWorld runStepMethods.
>> > Transcript flushDeferredEntry. "this is printing for #forceUpdate =
>> > false"
>> > self displayWorldSafely: aWorld].
>> >
>> >
>> > For #forceUpdate = true the endEntry mechanism remains unchanged and failing
>> > as before...
>> >
>> >
>> >
>> > --
>> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>> >
>> >
>> >
>>
>





12