[Vm-dev] Slang newbie question
Ronald Spengler
ron.spengler at gmail.com
Tue Sep 15 04:52:06 UTC 2009
Yikes. Looks like returning a string from the prim isn't easy either.
I googled and didn't find anything in particular; is there a generally
accepted approach to doing this?
I already have:
self returnTypeC: 'char *'.
The best I could find was TR suggesting that one find a primitive that
already knew what to do with a char* (I grok that the pointer is
unsafe if garbage collection has occured:)
http://lists.squeakfoundation.org/pipermail/vm-dev/2005-December/000387.html
On Mon, Sep 14, 2009 at 8:30 PM, Andreas Raab <andreas.raab at gmx.de> wrote:
>
> Ronald Spengler wrote:
>>
>> Okay, reread the code, realized that the comments actually answer
>> every question I have except "how do I guarantee that the garbage
>> collector doesn't run"
>
> Don't call functions that allocate objects. A GC may happen if you call a
> function that allocates an object, including, but not limited to,
> instantiateClassIndexableSize(); makePointwithXValueyValue() and others.
>
> This will hopefully change in the future to a scheme where primitives
> *never* cause GC unless explicitly requested (and rather fail the allocation
> and have the plugin deal with that failure) but for now, the only thing you
> can do is to either avoid allocations altogether (which isn't as bad as it
> sounds since mostly you don't need to allocate Squeak objects from
> primitives) or do it at the end of the primitive where you can ignore the
> inputs (since they have been used already) and just construct a result, or
> do proper remapping of your inputs (listed in increasing number of
> difficulty).
>
> If someone has a list of interpreter proxy functions handy, we can tell you
> which ones may cause GC and which ones are GC-safe.
>
> Cheers,
> - Andreas
>
>> On Mon, Sep 14, 2009 at 7:36 PM, Ronald Spengler <ron.spengler at gmail.com>
>> wrote:
>>>
>>> Thanks Dave! That helps a lot. This snippet scares me a little bit
>>> though:
>>> "in a section of code in which the garbage collector is guaranteed not to
>>> run"
>>>
>>> I realize now that:
>>> - I don't know how to guarantee that the gc won't run
>>> - My C library will take it's sweet time running, and it's runtime is
>>> a function of it's input, could be forever in the extreme case.
>>>
>>> So, to be safe, on the C side of things, should I copy the string
>>> ASAP? Or does C code escape the garbage collector? Is it safe to
>>> malloc()?
>>>
>>> Thanks again for your help, and please forgive my ignorance.
>>>
>>>
>>> On Sun, Sep 13, 2009 at 6:56 PM, David T. Lewis <lewis at mail.msen.com>
>>> wrote:
>>>>
>>>> On Sun, Sep 13, 2009 at 01:23:32PM -0700, Ronald Spengler wrote:
>>>>>
>>>>> Hello everyone.
>>>>> I have a named primitive, and I need to send a ByteString to it, to be
>>>>> processed and returned by an external library. To get a string into
>>>>> Slang,
>>>>> should I send it #asByteArray, and would that let me treat the bytes as
>>>>> integers on the stack? I'm basically trying to get a char* on the other
>>>>> side.
>>>>
>>>> You can use the ByteString as a parameter to the primitive, no problem.
>>>> The only tricky bit is that C expects null terminated strings, so you
>>>> need
>>>> to copy the contents of the ByteString into a null terminated array
>>>> before
>>>> you can let it be used by the C library as a char *.
>>>>
>>>> I'm sure there are lots of examples, but you can look at
>>>> OSProcessPlugin>>cStringFromString: and
>>>> OSProcess>>transientCStringFromString:
>>>> for examples of how to copy the string buffer into a null terminated
>>>> buffer
>>>> for use in C. Look at senders of these two methods for examples of
>>>> primitives
>>>> that pass strings as parameters. (OSProcessPlugin is on SqueakSource if
>>>> you
>>>> do not have it).
>>>>
>>>> Following are a couple of examples taken from OSPP. In both cases, a
>>>> buffer
>>>> is allocated with size one greater than the string length, and the
>>>> contents
>>>> of the Smalltalk string are copied into the buffer space with a trailing
>>>> null terminator. The #primitiveChdir example allocates a new Smalltalk
>>>> string to use for the buffer, and #primitivePutEnv uses malloc to
>>>> allocate
>>>> the new buffer (because in this case the buffer must be "permanently"
>>>> valid
>>>> after the primitive exits).
>>>>
>>>> primitiveChdir
>>>> "Call chdir(2) to change current working directory to the
>>>> specified path string. Answer
>>>> nil for success, or errno on failure."
>>>>
>>>> | path errno |
>>>> self export: true.
>>>> self var: 'path' type: 'char *'.
>>>> self var: 'errno' type: 'extern int'.
>>>> path := self transientCStringFromString: (interpreterProxy
>>>> stackObjectValue: 0).
>>>> (self chdir: path)
>>>> ifTrue: [interpreterProxy pop: 2; push: interpreterProxy
>>>> nilObject]
>>>> ifFalse: [interpreterProxy pop: 2; pushInteger: errno].
>>>>
>>>> transientCStringFromString: aString
>>>> "Answer a new null-terminated C string copied from aString.
>>>> The string is allocated in object memory, and will be moved
>>>> without warning by the garbage collector. Any C pointer
>>>> reference the the result is valid only until the garbage
>>>> collector next runs. Therefore, this method should only be used
>>>> within a single primitive in a section of code in which the
>>>> garbage collector is guaranteed not to run. Note also that
>>>> this method may itself invoke the garbage collector prior
>>>> to allocating the new C string.
>>>>
>>>> Warning: The result of this method will be invalidated by the
>>>> next garbage collection, including a GC triggered by creation
>>>> of a new object within a primitive. Do not call this method
>>>> twice to obtain two string pointers."
>>>>
>>>> | len stringPtr newString cString |
>>>> self returnTypeC: 'char *'.
>>>> self var: 'stringPtr' declareC: 'char *stringPtr'.
>>>> self var: 'cString' declareC: 'char *cString'.
>>>> len := interpreterProxy sizeOfSTArrayFromCPrimitive:
>>>> (interpreterProxy arrayValueOf: aString).
>>>> "Allocate space for a null terminated C string."
>>>> interpreterProxy pushRemappableOop: aString.
>>>> newString := interpreterProxy
>>>> instantiateClass: interpreterProxy classString
>>>> indexableSize: len + 1.
>>>> stringPtr := interpreterProxy arrayValueOf: interpreterProxy
>>>> popRemappableOop.
>>>> cString := interpreterProxy arrayValueOf: newString.
>>>> "Point to the actual C string."
>>>> self cCode: '(char *)strncpy(cString, stringPtr, len)'.
>>>> "Make a copy of the string."
>>>> cString at: (len) put: 0.
>>>> "Null terminate the C string."
>>>> ^ cString
>>>>
>>>> primitivePutEnv
>>>> "Set an environment variable using a string of the form
>>>> 'KEY=value'. This
>>>> implementation allocates a C string using malloc to allocate from
>>>> the C heap
>>>> (using cStringFromString rather than transientCStringFromString).
>>>> This
>>>> is necessary because the C runtime library does not make a copy of
>>>> the
>>>> string into separately allocated environment memory."
>>>>
>>>> | cStringPtr keyValueString |
>>>> self export: true.
>>>> self var: 'cStringPtr' declareC: 'char *cStringPtr'.
>>>> keyValueString := interpreterProxy stackObjectValue: 0.
>>>> cStringPtr := self cStringFromString: keyValueString.
>>>> ((self putenv: cStringPtr) == 0) "Set environment
>>>> variable."
>>>> ifTrue: [interpreterProxy pop: 2; push: keyValueString]
>>>> ifFalse: [^ interpreterProxy primitiveFail]
>>>>
>>>> cStringFromString: aString
>>>> "Answer a new null-terminated C string copied from aString. The C
>>>> string
>>>> is allocated from the C runtime heap. See
>>>> transientCStringFromString for
>>>> a version which allocates from object memory.
>>>> Caution: This may invoke the garbage collector."
>>>>
>>>> | len sPtr cString |
>>>> self returnTypeC: 'char *'.
>>>> self var: 'sPtr' declareC: 'char *sPtr'.
>>>> self var: 'cString' declareC: 'char *cString'.
>>>> sPtr := interpreterProxy arrayValueOf: aString.
>>>> len := interpreterProxy sizeOfSTArrayFromCPrimitive: sPtr.
>>>> cString := self callocWrapper: len + 1 size: 1. "Space for
>>>> a null terminated C string."
>>>> self cCode: '(char *) strncpy (cString, sPtr, len)'. "Copy the
>>>> string."
>>>> ^ cString
>>>>
>>>> callocWrapper: count size: objectSize
>>>> "Using malloc() and calloc() is something I would like to avoid,
>>>> since it is
>>>> likely to cause problems some time in the future if somebody
>>>> redesigns
>>>> object memory allocation. This wrapper just makes it easy to find
>>>> senders
>>>> of calloc() in my code. -dtl"
>>>>
>>>> self returnTypeC: 'void *'.
>>>> ^ self cCode: 'calloc(count, objectSize)'
>>>>
>>>> Dave
>>>>
>>>
>>>
>>> --
>>> Ron
>>>
>>
>>
>>
>
--
Ron
More information about the Vm-dev
mailing list