This repository has been archived by the owner on Jan 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
TFCallbacksTest.class.st
191 lines (147 loc) · 5.12 KB
/
TFCallbacksTest.class.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
Class {
#name : #TFCallbacksTest,
#superclass : #TFTestCase,
#instVars : [
'oldExceptionHandler'
],
#category : #'ThreadedFFI-Tests'
}
{ #category : #utils }
TFCallbacksTest >> callCallback: aCallback [
| functionDefinition function |
functionDefinition := TFFunctionDefinition
parameterTypes: {}
returnType: TFBasicType void.
function := TFExternalFunction fromAddress: aCallback getHandle
definition: functionDefinition.
runner invokeFunction: function withArguments: {}
]
{ #category : #'instance creation' }
TFCallbacksTest >> newTestCallbackDoing: aBlock [
^ TFCallback
forCallback: aBlock
parameters: {}
returnType: TFBasicType void
runner: runner.
]
{ #category : #running }
TFCallbacksTest >> setUp [
super setUp.
oldExceptionHandler := runner exceptionHandler.
runner exceptionHandler: TFTestCallbackExceptionHandler new
]
{ #category : #running }
TFCallbacksTest >> tearDown [
runner exceptionHandler: oldExceptionHandler.
super tearDown
]
{ #category : #tests }
TFCallbacksTest >> testCallbackFromOldSessionFailsReturn [
"If a callback survives a session (e.g., an image is saved and reopened), returning it will fail.
This test validates a surviving callback throws a meaningful exception when trying to return in this case."
| smain s1 c1 |
smain := Semaphore new.
s1 := Semaphore new.
c1 := self newTestCallbackDoing: [ smain signal. s1 wait ].
[ self callCallback: c1 ] fork.
"wait until callback starts"
smain wait.
"Release the runner as if a new session started"
runner release.
"make callback finish and wait until it fails"
s1 signal.
runner exceptionHandler wait.
self assert: runner exceptionHandler lastException class equals: TFInvalidSessionCallbackReturn.
self assert: runner exceptionHandler lastException callback equals: c1.
]
{ #category : #tests }
TFCallbacksTest >> testCallbackNotRespectingLIFOOrderFailsReturn [
| smain s1 c1 s2 c2 returned |
smain := Semaphore new.
true ifTrue: [ ^ self skip ].
returned := false.
s1 := Semaphore new.
c1 := self newTestCallbackDoing: [ smain signal. s1 wait ].
s2 := Semaphore new.
c2 := self newTestCallbackDoing: [ smain signal. s2 wait ].
[ self callCallback: c1. returned := true ] fork.
[ self callCallback: c2 ] fork.
"wait until both callbacks arrive"
smain wait; wait.
"make callback 1 to finish while callback 2 is suspended"
s1 signal.
"Wait until C1 tries to return"
100 milliSecond wait.
"It should not return until C2 returns"
self deny: returned.
"Signal s2 to make c2 return"
s2 signal.
"Wait until C1 tries to return"
100 milliSecond wait.
self assert: returned.
]
{ #category : #tests }
TFCallbacksTest >> testReentrantCalloutsDuringCallback [
| callback fun returnValue |
"Avoid running this test before the image side support handles this case.
Otherwise both the UI thread and the callback management thread will get blocked in a deadlock."
"true ifTrue: [ ^ self skip ]."
fun := TFExternalFunction
name: 'singleCallToCallback'
moduleName: self libraryPath
definition: (TFFunctionDefinition
parameterTypes: {TFBasicType pointer. TFBasicType sint32}
returnType: TFBasicType sint32).
callback := TFCallback
forCallback: [ :times |
times = 7
ifTrue: [ times ]
ifFalse: [ runner invokeFunction: fun withArguments: {callback getHandle. times + 1} ] ]
parameters: { TFBasicType sint32. }
returnType: TFBasicType sint32
runner: runner.
returnValue := runner invokeFunction: fun withArguments: {callback getHandle. 0}.
self assert: returnValue equals: 7
]
{ #category : #tests }
TFCallbacksTest >> testReentrantCalloutsDuringCallbackUsingSameProcessForCallbacks [
| callback fun returnValue |
"Avoid running this test before the image side support handles this case.
Otherwise both the UI thread and the callback management thread will get blocked in a deadlock."
"true ifTrue: [ ^ self skip ]."
fun := TFExternalFunction
name: 'singleCallToCallback'
moduleName: self libraryPath
definition: (TFFunctionDefinition
parameterTypes: {TFBasicType pointer. TFBasicType sint}
returnType: TFBasicType sint).
callback := TFCallback
forCallback: [ :times |
times = 7
ifTrue: [ times ]
ifFalse: [
runner invokeFunction: fun withArguments: {callback getHandle. times + 1} ] ]
parameters: { TFBasicType sint. }
returnType: TFBasicType sint
runner: runner.
callback runStrategy: TFCallbackSameProcessRunStrategy uniqueInstance.
returnValue := runner invokeFunction: fun withArguments: {callback getHandle. 0}.
self assert: returnValue equals: 7
]
{ #category : #tests }
TFCallbacksTest >> testSingleCalloutDuringCallback [
| callback fun returnValue |
callback := TFCallback
forCallback: [ :a | self shortCallout ]
parameters: { TFBasicType sint. }
returnType: TFBasicType sint
runner: runner.
fun := TFExternalFunction
name: 'singleCallToCallback'
moduleName: self libraryPath
definition: (TFFunctionDefinition
parameterTypes: {TFBasicType pointer. TFBasicType sint}
returnType: TFBasicType sint).
returnValue := runner invokeFunction: fun withArguments: {callback getHandle. 3}.
self assert: returnValue equals: 42
]