Hi all --
Yes, the new primitive "primitiveScreenScaleFactor" is not yet functional, but we can do something to at least improve the manual way of scaling fonts (and widgets and tools) so that your favorite high-DPI display can be used in Squeak (i.e. "sharp fonts") without letting the operating system scale (i.e. "blurry fonts").
Here is an older discussion: http://lists.squeakfoundation.org/pipermail/squeak-dev/2020-September/21160… [http://lists.squeakfoundation.org/pipermail/squeak-dev/2020-September/21160…]
Please find attached a change set for your to try out. Load it and then click Extras > Scale Factor > 200%. Or do-it in a workspace: "Display scaleFactor: 1.25". Note that 100%, 125%, and 150% will still use our pre-rendered "Bitmap DejaVu Sans" fonts. Other scale factors rely on "BitstreamVeraSans(Mono)".
I have still a little bit more testing to do considering the use of FontImporterTool. :-)
It's still not perfect. But at least we now have a one-click solution for scale factors bigger than 150%.
Best,
Marcel
Christoph Thiede uploaded a new version of SUnit to project The Inbox:
http://source.squeak.org/inbox/SUnit-ct.130.mcz
==================== Summary ====================
Name: SUnit-ct.130
Author: ct
Time: 24 September 2020, 11:28:31.626426 am
UUID: 43df5072-3a69-634c-946a-ec59903c2519
Ancestors: SUnit-pre.122
Deprecates TestResult class >> #error and replaces sends to it with #exError. It is a bad and erroneous practice to override #error in a way that does not signal an error but returns a class object.
=============== Diff against SUnit-pre.122 ===============
Item was changed:
----- Method: SUnitTest>>testDialectLocalizedException (in category 'tests') -----
testDialectLocalizedException
self
should: [TestResult signalFailureWith: 'Foo']
raise: TestResult failure.
self
should: [TestResult signalErrorWith: 'Foo']
+ raise: TestResult exError.!
- raise: TestResult error.
-
- !
Item was changed:
----- Method: SUnitTest>>testException (in category 'tests') -----
testException
self
should: [self error: 'foo']
+ raise: TestResult exError!
- raise: TestResult error
- !
Item was changed:
----- Method: SUnitTest>>testWithExceptionDo (in category 'tests') -----
testWithExceptionDo
self
should: [self error: 'foo']
+ raise: TestResult exError
- raise: TestResult error
withExceptionDo: [:exception |
self assert: (exception description includesSubstring: 'foo')
+ ]!
- ]
- !
Item was changed:
----- Method: TestResult class>>error (in category 'exceptions') -----
error
+
+ self deprecated: 'ct: Send #exError to retrieve an exception class or #error: to signal an error, depending on what you need.'.
+ ^ self exError!
- ^self exError
- !
Item was changed:
----- Method: TestResult class>>signalErrorWith: (in category 'exceptions') -----
signalErrorWith: aString
+ ^ self exError signal: aString!
- self error signal: aString
- !
Hi All,
here's a summary of bugs in the current termination procedure:
1. #isTerminated and #isSuspended fail to report correctly - in [1]
2. active process termination bug causing an image crash - in [2]
3. nested unwind bug: skipping some ensure blocks - in [3]
4. likely a bug: a failure to complete nested unwind blocks halfway thru
execution - also in [3]
5. Christoph's discovery: a failure to correctly close the debugger in case
of error or non-local return in unwind - in [4]
6. inconsistent semantics of unwinding protected blocks during active vs.
suspended process termination
(not reported yet - different sets of unwind blocks are executed depending
on whether a process terminates itself or whether some other process
initiates its termination; the proposed solution unifies active and
suspended process termination and is consistent with Visual Works semantics
(and goes beyond))
[1]
http://forum.world.st/The-Inbox-Kernel-jar-1376-mcz-td5127335.html#a5127336
[2]
http://forum.world.st/A-bug-in-active-process-termination-crashing-image-td…
[3]
http://forum.world.st/Another-bug-in-Process-terminate-in-unwinding-context…
[4]
http://forum.world.st/Bug-in-Process-gt-gt-terminate-Returning-from-unwind-…
I'd like to propose a solution - a rewrite of #terminate - addressing all of
the above issues. Main points of the solution are:
(i) why differentiate active and suspended process termination? I propose to
unify the two: to suspend the active process and terminate it as a suspended
process. As a result the semantics/outcome of the unwind algorithm will be
the same in both cases. Fixes 2 and 6.
(ii) the above leads to a susbstantial simplification of #isTerminated - a
process is terminated when at the last instruction of its bottom context.
Fixes 1.
(iii) unwind algorithm for incomplete unwind blocks is extended to execute
the outer-most instead of the inner-most incomplete unwind context. Fixes 3
and 4.
(iv) indirect termination via #popTo is replaced with a analogue of
#unwindTo: using Eliot's ingenious #runUntilErrorOrReturnFrom: the same way
as in (iii). I'd very much appreciate if someone commented this part because
I haven't found any clue why the indirect termination has implemented using
#popTo and not #runUntilErrorOrReturnFrom. Fixes 5.
I enclose a set of examples used to compare the current Squeak semantics
with the proposed one (and with VW) that can be used to build test cases.
Here's a commented code. I'll be happy to provide detailed step-by-step
guidance if you find this whole idea interesting and worth implementing. I'm
convinced at least parts of the proposal should be integrated as simple
fixes of the current bugs. Thank you for your patience if you're still
reading :) Any feedback extremely welcome. A changeset is enclosed for your
convenience here: Fix_terminate_v2.cs
<http://forum.world.st/file/t372955/Fix_terminate_v2.cs>
Process >> terminate
"Stop the process that the receiver represents so that it answers true to
#isTerminated.
Unwind to execute pending and unfinished ensure:/ifCurtailed: blocks
before terminating.
If the process is in the middle of a critical: critical section, release
it properly."
| ctxt unwindBlock oldList outerMost |
self isActiveProcess ifTrue: [
"If terminating the active process, suspend it first and terminate it as a
suspended process.
Nested #terminate messages could derail the termination so let's enclose
it in #ensure."
[[] ensure: [self terminate]] fork.
^self suspend].
"Always suspend the process first so it doesn't accidentally get woken up.
N.B. If oldList is a LinkedList then the process is runnable. If it is a
Semaphore/Mutex et al
then the process is blocked, and if it is nil then the process is already
suspended."
oldList := self suspend.
suspendedContext ifNotNil:
["Release any method marked with the <criticalSection> pragma.
The argument is whether the process is runnable."
self releaseCriticalSection: (oldList isNil or: [oldList class ==
LinkedList]).
"If terminating a process halfways through an unwind, try to complete that
unwind block first;
if there are multiple such nested unwind blocks, try to complete the
outer-most one; the inner
blocks will be completed in the process."
ctxt := suspendedContext.
[(ctxt := ctxt findNextUnwindContextUpTo: nil) isNil] whileFalse:
"Contexts under evaluation have already set their complete (tempAt: 2) to
true."
[(ctxt tempAt:2) ifNotNil: [outerMost := ctxt]].
outerMost ifNotNil: [
"This is the outer-most unwind context currently under evaluation;
let's find an inner context executing outerMost's argument block (tempAt:
1)"
(suspendedContext findContextSuchThat: [:ctx |
ctx closure == (outerMost tempAt: 1)]) ifNotNil: [:inner |
"Let's finish the unfinished unwind context only (i.e. up to inner) and
return here"
suspendedContext runUntilErrorOrReturnFrom: inner.
"Update the receiver's suspendedContext (the previous step reset its
sender to nil)"
suspendedContext := outerMost]].
"Now all unwind blocks caught halfway through have been completed;
let's execute the ones still pending. Note: #findNextUnwindContextUpTo:
starts
searching from the receiver's sender but the receiver itself may be an
unwind context."
ctxt := suspendedContext.
ctxt isUnwindContext ifFalse: [ctxt := ctxt findNextUnwindContextUpTo:
nil].
[ctxt isNil] whileFalse: [
(ctxt tempAt: 2) ifNil: [
ctxt tempAt: 2 put: true.
unwindBlock := ctxt tempAt: 1.
"Create a context for the unwind block and execute it on the unwind
block's stack.
Note: using #value instead of #runUntilErrorOrReturnFrom: would lead to
executing
the unwind on the wrong stack preventing the correct execution of
non-local returns."
suspendedContext := unwindBlock asContextWithSender: ctxt.
suspendedContext runUntilErrorOrReturnFrom: suspendedContext].
ctxt := ctxt findNextUnwindContextUpTo: nil].
"Set the context to its endPC and its sender to nil for the benefit of
isTerminated."
ctxt := suspendedContext.
ctxt terminateTo: nil.
ctxt pc: ctxt endPC]
Process >> isTerminated
"Answer if the receiver is terminated. A process is considered terminated
if the suspendedContext is the bottomContext and the pc is at the endPC"
self isActiveProcess ifTrue: [^ false].
^suspendedContext isNil or: [
suspendedContext isBottomContext and: [
suspendedContext isDead or: [suspendedContext atEnd]]]
Process >> isSuspended
"A process is suspended if it has non-nil suspendedContext (e.g. new or
previously suspended with the suspend primitive) and is not terminated or
waiting in a scheduler or a semaphore queue (i.e. is not runnable or
blocked)."
^myList isNil
and: [suspendedContext notNil
and: [self isTerminated not]]
Debugger >> windowIsClosing
"My window is being closed; clean up. Restart the low space watcher.
If the debugged process is a do-it, it may do a non-local return escaping
termination so we need to insert a #terminate context under the do-it
to make sure the debugged UI process will terminate."
| doItContext terminateContext |
(interruptedProcess == nil or: [interruptedProcess suspendedContext isNil])
ifTrue: [^ self].
"find a do-it context; answer nil if it doesn't exist in the sender chain"
doItContext := interruptedProcess suspendedContext
findContextSuchThat: [:ctx |
ctx methodClass = UndefinedObject and: [
ctx selector = #DoIt and: [
ctx closure isNil]]].
doItContext ifNotNil: [ "it exists so let's insert a #terminate context"
terminateContext := Context
sender: doItContext sender
receiver: interruptedProcess
method: (Process>>#terminate)
arguments: {}.
doItContext privSender: terminateContext ].
interruptedProcess terminate.
interruptedProcess := nil.
contextStack := nil.
receiverInspector := nil.
contextVariablesInspector := nil.
Smalltalk installLowSpaceWatcher. "restart low space handler"
A few examples to illustrate the idea (many more enclosed here
Some_examples_to_examine_terminate_bugs.txt
<http://forum.world.st/file/t372955/Some_examples_to_examine_terminate_bugs.…>
):
Ex. 1:
| p |
"Suspend process inside ensure block and make sure x1 x2 and x3 are printed.
Currently only x1 is printed."
p := [
[
[ ] ensure: [
[ ] ensure: [
Processor activeProcess suspend.
Transcript show: 'x1'].
Transcript show: 'x2']
] ensure: [
Transcript show: 'x3']
] fork.
Processor yield.
p terminate
---> x1 (instead of x1 x2 x3)
Ex. 2:
| p |
Currently only x3 is printed."
p := [
[
[ ] ensure: [
[ ] ensure: [
Processor activeProcess terminate.
Transcript show: 'x1'].
"terminate active process inside ensure block and make sure x1 x2 and x3
are printed.
Transcript show: 'x2']
] ensure: [
Transcript show: 'x3']
] fork.
Processor yield
---> x3 (instead of x1 x2 x3)
Ex. 3:
"unwind after error inside ensure block and make sure x1 x2 and x3 are
printed.
[
[ ] ensure: [
[ ] ensure: [
self error.
Transcript show: 'x1'].
Transcript show: 'x2']
] ensure: [
^Transcript show: 'x3']
---> x3 (instead of x1 x2 x3)
Note: nested errors during unwind follow #runUntilErrorOrReturnFrom: logic,
i.e. return exception and let user decide...
-----
^[^ Jaromir
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Christoph Thiede uploaded a new version of SUnit to project The Inbox:
http://source.squeak.org/inbox/SUnit-ct.124.mcz
==================== Summary ====================
Name: SUnit-ct.124
Author: ct
Time: 20 March 2020, 8:01:47.398237 pm
UUID: d936b6a0-e7c7-2742-a817-35fa6511a15a
Ancestors: SUnit-mt.121
Proposal for discussion: Adds assertion message #should:raise:then: to allow for interactively working with expected exceptions. If you like, we could also talk about #should:raise:that: (however, I still like BlockClosure >> #handles:*).
Examples:
TestCase new in: [:test |
test
should: [
test
should: [self error: #foo]
raise: Error
then: #pass]
raise: UnhandledError.
test
should: [self error: #foo]
raise: Error
then: [:ex |
test assert: #foo equals: ex messageText].
]
* See exception patterns: http://forum.world.st/The-Inbox-Kernel-ct-1292-mcz-tp5109282p5109284.html
=============== Diff against SUnit-mt.121 ===============
Item was added:
+ ----- Method: TestCase>>should:raise:then: (in category 'accessing') -----
+ should: aBlock raise: anExceptionalEvent then: aHandlerBlock
+
+ | raised result |
+ raised := false.
+ result := aBlock
+ on: anExceptionalEvent
+ do: [:ex |
+ raised := true.
+ aHandlerBlock cull: ex].
+ self assert: raised.
+ ^ result!
Christoph Thiede uploaded a new version of SUnit to project The Inbox:
http://source.squeak.org/inbox/SUnit-ct.129.mcz
==================== Summary ====================
Name: SUnit-ct.129
Author: ct
Time: 24 September 2020, 10:42:52.868426 am
UUID: 92e68d23-8472-5d48-96d3-8435bd56ac14
Ancestors: SUnit-pre.122
Proposal: Catch warnings and halts in test case execution as well as Errors.
Catching (Error, Warning, Halt) is a common pattern to be (relatively) sure that no debugger will occur during an operation. For related usages, see Morph >> #fullBounds, WorldState >> #displayWorldSafely:, and many other places. IMO it is no desired behavior that the whole test execution, i.e. in a TestRunner, is interrupted because any method under test contains a halt or raises a DeprecationWarning, for example. Instead, the test should be listed as red.
For a similar discussion, see https://github.com/hpi-swa/smalltalkCI/issues/470. I believe we already had talked about this on squeak-dev, but if I remember correctly, I cannot find the thread again.
=============== Diff against SUnit-pre.122 ===============
Item was changed:
----- Method: TestCase>>timeout:after: (in category 'private') -----
timeout: aBlock after: seconds
"Evaluate the argument block. Time out if the evaluation is not
complete after the given number of seconds. Handle the situation
that a timeout may occur after a failure (during debug)"
| theProcess delay watchdog |
"the block will be executed in the current process"
theProcess := Processor activeProcess.
delay := Delay forSeconds: seconds.
"make a watchdog process"
watchdog := [
delay wait. "wait for timeout or completion"
theProcess ifNotNil:[ theProcess signalException:
(TestFailure new messageText: 'Test timed out') ]
] newProcess.
"Watchdog needs to run at high priority to do its job (but not at timing priority)"
watchdog priority: Processor timingPriority-1.
"catch the timeout signal"
watchdog resume. "start up the watchdog"
+ ^[aBlock on: TestFailure, (Error, Warning, Halt) do: [:ex|
- ^[aBlock on: TestFailure, Error, Halt do:[:ex|
theProcess := nil.
ex pass.
]] ensure:[ "evaluate the receiver"
theProcess := nil. "it has completed, so ..."
delay delaySemaphore signal. "arrange for the watchdog to exit"
]!
Item was added:
+ ----- Method: TestResult class>>exAllErrors (in category 'exceptions') -----
+ exAllErrors
+ ^ self exError, Warning, Halt
+ !
Item was changed:
----- Method: TestResult>>runCase: (in category 'running') -----
runCase: aTestCase
| testCasePassed timeToRun |
testCasePassed := true.
[timeToRun := [aTestCase runCase] timeToRunWithoutGC]
on: self class failure
do: [:signal |
failures add: aTestCase.
testCasePassed := false.
signal return: false]
+ on: self class exAllErrors
- on: self class error
do: [:signal |
errors add: aTestCase.
testCasePassed := false.
signal return: false].
testCasePassed ifTrue: [passed add: aTestCase].
self durations at: aTestCase put: timeToRun.!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-mt.1437.mcz
==================== Summary ====================
Name: Kernel-mt.1437
Author: mt
Time: 26 December 2021, 8:21:42.050619 am
UUID: 28086a0b-8543-4f44-9c9f-7e63d3028f7f
Ancestors: Kernel-ct.1436
Makes #acceptsLoggingOfCompilation a preference that can be disabled. See commentary #logCompiledSources. Classes can still override this preference but should follow a "super acceptsLoggingOfCompilation and: [...]" pattern.
Thanks to Tim (tpr) for figuring out the mechanics and testing it throughout the year!
=============== Diff against Kernel-ct.1436 ===============
Item was changed:
Behavior subclass: #ClassDescription
instanceVariableNames: 'instanceVariables organization'
+ classVariableNames: 'AcceptsLoggingOfCompilation TraitImpl'
- classVariableNames: 'TraitImpl'
poolDictionaries: ''
category: 'Kernel-Classes'!
!ClassDescription commentStamp: '<historical>' prior: 0!
I add a number of facilities to basic Behaviors:
Named instance variables
Category organization for methods
The notion of a name of this class (implemented as subclass responsibility)
The maintenance of a ChangeSet, and logging changes on a file
Most of the mechanism for fileOut.
I am an abstract class, in particular, my facilities are intended for inheritance by two subclasses, Class and Metaclass.
The slots 'organization' and 'methodDict' should ONLY be accessed by message in order for things to work during ImageSegment>>discoverActiveClasses (q.v.).!
Item was added:
+ ----- Method: ClassDescription class>>logCompiledSources (in category 'compiling') -----
+ logCompiledSources
+ <preference: 'Log compilations to changes file'
+ category: 'general'
+ description: 'If enabled, the code being compiled will get logged to the changes file. Disabling allows loading code with no changes file in use. See also #warnIfNoChangesFile and #warnIfNoSourcesFile and #readDocumentAtStartup. You can configure a silent image/DoItFirst for, e.g., server environments.'
+ type: #Boolean>
+
+ ^ AcceptsLoggingOfCompilation ifNil: [true]!
Item was added:
+ ----- Method: ClassDescription class>>logCompiledSources: (in category 'compiling') -----
+ logCompiledSources: aBoolean
+ ^ AcceptsLoggingOfCompilation := aBoolean!
Item was changed:
----- Method: ClassDescription>>acceptsLoggingOfCompilation (in category 'compiling') -----
acceptsLoggingOfCompilation
"weird name is so that it will come lexically before #compile, so that a clean build can make it through. 7/7/96 sw"
+ ^AcceptsLoggingOfCompilation ifNil: [true]!
- ^ true!