[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