From 0e7b10b5646713192bac83428bd92e20d7aebfe8 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Wed, 8 Jan 2025 14:55:33 +0100 Subject: [PATCH] - When there are concurrently processes using the same FFI methods, they might produce a situation where registered methods are wrong. The registered method might ends with a FFI method pointing to an FFI method and not to the original mehtod. - Adding a test also. --- .../TFUFFIConcurrencyTest.class.st | 80 +++++++++++++++++++ src/UnifiedFFI/FFICalloutAPI.class.st | 2 +- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/ThreadedFFI-UFFI-Tests/TFUFFIConcurrencyTest.class.st diff --git a/src/ThreadedFFI-UFFI-Tests/TFUFFIConcurrencyTest.class.st b/src/ThreadedFFI-UFFI-Tests/TFUFFIConcurrencyTest.class.st new file mode 100644 index 00000000000..51a36e61b66 --- /dev/null +++ b/src/ThreadedFFI-UFFI-Tests/TFUFFIConcurrencyTest.class.st @@ -0,0 +1,80 @@ +Class { + #name : #TFUFFIConcurrencyTest, + #superclass : #TFUFFITestCase, + #instVars : [ + 'p1', + 'p2' + ], + #category : #'ThreadedFFI-UFFI-Tests' +} + +{ #category : #'ffi-calls' } +TFUFFIConcurrencyTest >> booleanToInt: boolean [ + + ^ self ffiCall: #(int id_int(Boolean boolean)) +] + +{ #category : #'ffi-calls' } +TFUFFIConcurrencyTest >> methodCallingBooleanToInt [ + + ^ self booleanToInt: true +] + +{ #category : #running } +TFUFFIConcurrencyTest >> tearDown [ + + FFIMethodRegistry uniqueInstance resetSingleClass: self class. + + p1 ifNotNil: #terminate. + p2 ifNotNil: #terminate. + + (self class >> #booleanToInt: hasProperty: #ffiNonCompiledMethod) + ifTrue: [ + self class compile: ' +booleanToInt: boolean + + ^ self ffiCall: #(int id_int(Boolean boolean)) ' ]. + super tearDown +] + +{ #category : #tests } +TFUFFIConcurrencyTest >> testConcurrentlyCompiling [ + + | barrier finish installedMethod nonCompiledMethod | + self deny: + (self class >> #booleanToInt: hasProperty: #ffiNonCompiledMethod). + + barrier := Semaphore new. + finish := Semaphore new. + + + p1 := [ + [ + barrier wait. + self methodCallingBooleanToInt. + finish signal ] repeat ] forkAt: 39. + + p2 := [ + [ + barrier wait. + self methodCallingBooleanToInt. + finish signal ] repeat ] forkAt: 39. + + 500 timesRepeat: [ + + FFIMethodRegistry uniqueInstance resetSingleClass: self class. + + barrier + signal; + signal. + finish + wait; + wait. + + installedMethod := self class >> #booleanToInt:. + nonCompiledMethod := installedMethod propertyAt: + #ffiNonCompiledMethod. + + self assert: (installedMethod hasProperty: #ffiNonCompiledMethod). + self deny: (nonCompiledMethod hasProperty: #ffiNonCompiledMethod) ] +] diff --git a/src/UnifiedFFI/FFICalloutAPI.class.st b/src/UnifiedFFI/FFICalloutAPI.class.st index fdde299f456..be02fc57ab1 100644 --- a/src/UnifiedFFI/FFICalloutAPI.class.st +++ b/src/UnifiedFFI/FFICalloutAPI.class.st @@ -125,7 +125,7 @@ FFICalloutAPI >> function: functionSignature library: moduleNameOrLibrary [ methodClass: sender methodClass. "Replace with generated ffi method, but save old one for future use" ffiMethod propertyAt: #ffiNonCompiledMethod - put: (sender methodClass methodDict at: sender selector). "For senders search, one need to keep the selector in the properties" + put: sender method. "For senders search, one need to keep the selector in the properties" ffiMethod propertyAt: #ffiMethodSelector put: ffiMethodSelector. sender methodClass methodDict at: sender selector put: ffiMethod. "Register current method as compiled for ffi" FFIMethodRegistry uniqueInstance registerMethod: ffiMethod. "Resend"