Skip to content

Commit 0903ade

Browse files
authored
Merge pull request #3178 from guillep/3157-Partial-Fix-Experiment-to-test-FreeType-Problem-backport
Fix for corrupted fonts in Pharo7
2 parents 12b1738 + f7f9600 commit 0903ade

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

src/FreeType-Tests/FreeTypeCacheTest.class.st

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,59 @@ FreeTypeCacheTest >> testFreeTypeCacheEntry [
118118
self assert: f = f3. "equality not based on nextLink"
119119
]
120120

121+
{ #category : #tests }
122+
FreeTypeCacheTest >> testGlyphAccessIsThreadSafe [
123+
"This test tests that concurrent glyph access does not break FreeType's cache.
124+
Basically the test renders a Rubric morph in concurrent green threads on a big string, and then we compare that the drawn morphs should be bit identical.
125+
We have made some measurements and observed that putting three concurrent processes made the chance of hitting the bug to almost 94%.
126+
We arrived to this 94% by (repeteadly) running this test 100 times and observing the number of broken cases (and calculating the average).
127+
128+
By applying some probabilities, we concluded that running it just 5 times makes it highly reproducible.
129+
130+
p := 0.94 asScaledDecimal.
131+
hittingTheBug := 0.
132+
notHittingTheBug := 1 - hittingTheBug.
133+
4 timesRepeat: [
134+
hittingTheBug := hittingTheBug + (notHittingTheBug * p).
135+
notHittingTheBug := 1 - hittingTheBug ].
136+
hittingTheBug.
137+
138+
After 5 iterations, hittingTheBug = 0.99999969s8.
139+
140+
Still, this test is weak in terms of dependencies because it depends in the rendering of Morphic and Rubrik.
141+
However, trying to simplify it makes the loop inside the testBlock tighter producing less race conditions, and thus it makes the probability of the test drop from 94% to <40%.
142+
"
143+
144+
| iterations concurrencyLevel |
145+
self timeLimit: 10 seconds.
146+
iterations := 5.
147+
concurrencyLevel := 3.
148+
149+
iterations timesRepeat: [
150+
| sem text canvases testBlock |
151+
sem := Semaphore new.
152+
text := (String loremIpsum: 25*1024).
153+
FreeTypeCache current removeAll.
154+
canvases := OrderedCollection new.
155+
156+
testBlock := [ | canvas |
157+
canvas := FormCanvas extent: 1000@1000.
158+
canvases add: canvas.
159+
(RubScrolledTextMorph new)
160+
setText: text;
161+
font: StandardFonts codeFont;
162+
bounds: (0@0 corner: canvas form extent);
163+
fullDrawOn: canvas.
164+
sem signal ].
165+
166+
concurrencyLevel timesRepeat: [ testBlock forkAt: 39 ].
167+
concurrencyLevel timesRepeat: [ sem wait ].
168+
169+
self assert: (canvases collect: [ :each | each form bits ] as: Set) size equals: 1 ].
170+
171+
172+
]
173+
121174
{ #category : #tests }
122175
FreeTypeCacheTest >> testInstanceInitialization [
123176
self assert: (cache instVarNamed: #maximumSize) = FreeTypeCache defaultMaximumSize.

src/FreeType/FreeTypeFont.class.st

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ Class {
1414
'cachedAscent',
1515
'cachedDescent',
1616
'subPixelPositioned',
17-
'symbolFont'
17+
'symbolFont',
18+
'mutex'
1819
],
1920
#pools : [
2021
'FT2Constants',
@@ -383,12 +384,13 @@ FreeTypeFont >> glyphOf: aCharacter colorValue: aColorValue mono: monoBoolean su
383384
charCode: aCharacter asUnicode asInteger
384385
type: ((1+sub) << 32) + aColorValue
385386
ifAbsentPut: [
386-
FreeTypeGlyphRenderer current
387-
glyphOf: aCharacter
388-
colorValue: aColorValue
389-
mono: monoBoolean
390-
subpixelPosition: sub
391-
font: self]
387+
self mutex critical: [
388+
FreeTypeGlyphRenderer current
389+
glyphOf: aCharacter
390+
colorValue: aColorValue
391+
mono: monoBoolean
392+
subpixelPosition: sub
393+
font: self]]
392394

393395

394396
]
@@ -664,14 +666,21 @@ FreeTypeFont >> mode41GlyphOf: aCharacter colorValue: aColorValue mono: monoBool
664666
charCode: aCharacter asUnicode asInteger
665667
type: (FreeTypeCacheGlyph + sub)
666668
ifAbsentPut: [
667-
FreeTypeGlyphRenderer current
668-
mode41GlyphOf: aCharacter
669-
colorValue: aColorValue
670-
mono: monoBoolean
671-
subpixelPosition: sub
672-
font: self]
669+
self mutex critical: [
670+
FreeTypeGlyphRenderer current
671+
mode41GlyphOf: aCharacter
672+
colorValue: aColorValue
673+
mono: monoBoolean
674+
subpixelPosition: sub
675+
font: self]]
676+
673677

678+
]
679+
680+
{ #category : #accessing }
681+
FreeTypeFont >> mutex [
674682

683+
^ mutex ifNil: [ mutex := Semaphore forMutualExclusion ]
675684
]
676685

677686
{ #category : #measuring }
@@ -790,12 +799,13 @@ FreeTypeFont >> subGlyphOf: aCharacter colorValue: aColorValue mono: monoBoolean
790799
charCode: aCharacter asUnicode asInteger
791800
type: FreeTypeCacheGlyphLCD + sub
792801
ifAbsentPut: [
793-
FreeTypeGlyphRenderer current
794-
subGlyphOf: aCharacter
795-
colorValue: aColorValue
796-
mono: monoBoolean
797-
subpixelPosition: sub
798-
font: self]
802+
self mutex critical: [
803+
FreeTypeGlyphRenderer current
804+
subGlyphOf: aCharacter
805+
colorValue: aColorValue
806+
mono: monoBoolean
807+
subpixelPosition: sub
808+
font: self]]
799809

800810

801811
]

0 commit comments

Comments
 (0)