[Vm-dev] Slang newbie question
Ronald Spengler
ron.spengler at gmail.com
Tue Sep 15 02:36:41 UTC 2009
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
More information about the Vm-dev
mailing list