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 <m@jaromir.net>: 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