[squeak-dev] FFI: FFI-Kernel-eem.227.mcz

commits at source.squeak.org commits at source.squeak.org
Fri Mar 10 02:22:18 UTC 2023


Eliot Miranda uploaded a new version of FFI-Kernel to project FFI:
http://source.squeak.org/FFI/FFI-Kernel-eem.227.mcz

==================== Summary ====================

Name: FFI-Kernel-eem.227
Author: eem
Time: 9 March 2023, 6:18:37.991467 pm
UUID: 9a37d6b3-7dc2-4d71-800b-8c5e834f88ad
Ancestors: FFI-Kernel-eem.226

Fix a bad thread safety bug with ExternalStructure and ExternalType class var StructTypes.

StructTypes is a WeakValueDIctionary from class name (subclass of ExternalStructure) to instance of ExternalStructureType.  Because the associations in  StructTypes have weak values and are the only reference in ther system to the manufactured ExternalStructureType instance, the garbage collector *will* set the association's valeu to nil sooner or later. This provides a nasty potential for the assert in ExternalUnknownType class>>#newTypeForUnknownNamed: to fail because two threads are both in the process of computing a new type via ExternalType class>>#structTypeNamed:.

The fix is to have ExternalStructure maintain a strong reference to the manufactured type in a class inst var, externalType, and to void this type, and the entry in StructTypes whenever a method is added or removed from the instance side of ExternalStructure.

=============== Diff against FFI-Kernel-eem.226 ===============

Item was changed:
  ExternalObject subclass: #ExternalStructure
  	instanceVariableNames: ''
  	classVariableNames: 'LogAccessorSourceCode'
  	poolDictionaries: 'ExternalTypePool FFIConstants'
  	category: 'FFI-Kernel'!
  ExternalStructure class
+ 	instanceVariableNames: 'compiledSpec byteAlignment externalType'!
- 	instanceVariableNames: 'compiledSpec byteAlignment'!
  
  !ExternalStructure commentStamp: 'eem 6/26/2019 15:26' prior: 0!
  An ExternalStructure is for representing external data that is
  - either a structure composed of different fields (a struct of C language)
  - or an alias for another type (like a typedef of C language)
  
  It reserves enough bytes of data for representing all the fields.
  
  The data is stored into the handle instance variable which can be of two different types:
  	- ExternalAddress
  		If the handle is an external address then the object described does not reside in the Smalltalk object memory.
  	- ByteArray
  		If the handle is a byte array then the object described resides in Smalltalk memory.
  
  
  Instance Variables (class side)
  	byteAlignment:		<Integer>
  	compiledSpec:		<WordArray>
  
  byteAlignment
  	- the required alignment for the structure
  
  compiledSpec
  	- the bit-field definiton of the structure in the ExternalType encoding understood by the VM's FFI call marshalling machinery.
  
  
  A specific structure is defined by subclassing ExternalStructure and specifying its #fields via a class side method.
  For example if we define a subclass:
  	ExternalStructure subclass: #StructExample
  		instanceVariableNames: ''
  		classVariableNames: ''
  		poolDictionaries: ''
  		category: 'garbage'.
  Then declare the fields like this:
      StructExample class compile: 'fields  ^#( (name ''char*'') (color ''ulong'') )' classified: 'garbage'.
  
  It means that this type is composed of two different fields:
  - a string (accessed thru the field #name)
  - and an unsigned 32bit integer (accessed thru the field #color).
  It represents the following C type:
     struct StructExample {char *name; uint32_t color; };
  
  The accessors for those fields can be generated automatically like this:
  	StructExample defineFields.
  As can be verified in a Browser:
  	StructExample browse.
  We see that name and color fields are stored sequentially in different zones of data.
  
  The total size of the structure can be verified with:
  	StructExample byteSize = (Smalltalk wordSize + 4).
  
  An ExternalStructure can also be used for defining an alias.
  The fields definition must contain only 2 elements: an eventual accessor (or nil) and the type.
  For example, We can define a machine dependent 'unsigned long' like this:
  	ExternalStructure subclass: #UnsignedLong
  		instanceVariableNames: ''
  		classVariableNames: ''
  		poolDictionaries: ''
  		category: 'garbage'.
  Then set the fields like this:
      UnsignedLong class compile: 'fields  ^(Smalltalk wordSize=4 or: [Smalltalk platformName=''Win64''])
  		ifTrue: [#(nil ''ulong'')] ifFalse: [#(nil ''ulonglong'')]' classified: 'garbage'.
  And verify the size on current platform:
  	UnsignedLong byteSize.
  	
  Then, the class names 'UnsignedLong' and 'StructExamples' acts as a type specification.
  They can be used for composing other types, and for defining prototype of external functions:
  
  LibraryExample>>initMyStruct: aStructExample name: name color: anInteger
  	<cdecl: void 'init_my_struct'( StructExample * char * UnsignedLong )>
  	self externalCallFailed
  
  
  !
  ExternalStructure class
+ 	instanceVariableNames: 'compiledSpec byteAlignment externalType'!
- 	instanceVariableNames: 'compiledSpec byteAlignment'!

Item was added:
+ ----- Method: ExternalStructure class>>addSelectorSilently:withMethod: (in category 'accessing method dictionary') -----
+ addSelectorSilently: selector withMethod: compiledMethod
+ 	"Override to void the strong reference to the externalType"
+ 	self voidExternalType.
+ 	^super addSelectorSilently: selector withMethod: compiledMethod!

Item was changed:
  ----- Method: ExternalStructure class>>externalType (in category 'external type') -----
  externalType
+ 	"Answer an external type describing the receiver as a structure.
+ 	 Keep a strong reference to it so that it doesn't get garbage collected
+ 	 from StructTypes immediately."
+ 	^externalType := ExternalType structTypeNamed: self name!
- 	"Return an external type describing the receiver as a structure"
- 	^ExternalType structTypeNamed: self name!

Item was added:
+ ----- Method: ExternalStructure class>>removeSelector: (in category 'accessing method dictionary') -----
+ removeSelector: selector
+ 	"Override to void the strong reference to the externalType"
+ 	self voidExternalType.
+ 	^super removeSelector: selector!

Item was added:
+ ----- Method: ExternalStructure class>>voidExternalType (in category 'private') -----
+ voidExternalType
+ 	externalType := nil.
+ 	ExternalType voidStructTypeFor: self!

Item was added:
+ ----- Method: ExternalType class>>voidStructTypeFor: (in category 'housekeeping') -----
+ voidStructTypeFor: anExternalStructureClass
+ 	(StructTypes associationAt: anExternalStructureClass name ifAbsent: [^self]) value: nil!



More information about the Squeak-dev mailing list