Skip to content

Sockets, libdispatch, and gnustep

Here are a client and server to show the use of GNUstep, libdispatch and sockets. This code was developed on FreeBSD.
There are three files:-

  • GNUmakefile
  • server.m
  • client.m

If you’re familiar with sockets code, you can see that the coding is vanilla sockets right up to the comments where the dispatch queues and sources are setup. The sockets code is based on code presented at: http://beej.us/guide/bgnet/ – so you can compare the two.

I used gnustep packages built on FreeBSD with clang – see my previous post. Assuming a working GNUstep and with libdispatch installed, just put all three files in a directory and type “gmake”.

GNUmakefile

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = client server
client_OBJC_FILES = client.m
server_OBJC_FILES = server.m

include $(GNUSTEP_MAKEFILES)/tool.make

server.m

#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
#define PORT "2305" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
 if (sa->sa_family == AF_INET) {
 return &(((struct sockaddr_in*)sa)->sin_addr);
 }
 return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
 int sockfd;
 struct addrinfo hints, *servinfo, *p;
 int yes=1;
 int rv;

memset(&hints, 0, sizeof hints);
 hints.ai_family = AF_UNSPEC;
 hints.ai_socktype = SOCK_STREAM;
 hints.ai_flags = AI_PASSIVE;

if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
 return 1;
 }

//loop to obtain a socket
 for(p = servinfo; p != NULL; p = p->ai_next) {
 if ((sockfd = socket(p->ai_family, p->ai_socktype,
 p->ai_protocol)) == -1) {
 perror("server: socket");
 continue;
 }

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
 sizeof(int)) == -1) {
 perror("setsockopt");
 exit(1);
 }

if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
 close(sockfd);
 perror("server: bind");
 continue;
 }

break;
 }

if (p == NULL) {
 fprintf(stderr, "server: failed to bind\n");
 return 2;
 }

freeaddrinfo(servinfo);

if (listen(sockfd, BACKLOG) == -1) {
 perror("listen");
 exit(1);
 }
 NSLog(@"Listening...");

//use a libdispatch queue to read data from the socket
 dispatch_semaphore_t exitsignal = dispatch_semaphore_create(0);
 dispatch_queue_t dq = dispatch_queue_create("data", NULL);
 dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, dq);

dispatch_source_set_event_handler(ds, ^{
 int new_fd;
 struct sockaddr_storage their_addr;
 socklen_t sin_size;
 char s[INET6_ADDRSTRLEN];
 sin_size = sizeof their_addr;

new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
 if (new_fd == -1) {
 perror("accept");
 }

inet_ntop(their_addr.ss_family,
 get_in_addr((struct sockaddr *)&their_addr),
 s, sizeof s);
 NSLog(@"server: got connection from %s\n", s);

// reply to client
 if (send(new_fd, "Hello, world!", 13, 0) == -1) perror("send");
 close(new_fd);
 });

dispatch_source_set_cancel_handler(ds, ^{
 int rfd = (int)dispatch_source_get_handle(ds);
 close(rfd);
 dispatch_semaphore_signal(exitsignal);
 });

dispatch_resume(ds);
 dispatch_main(); // suspend run loop waiting for blocks

dispatch_semaphore_wait(exitsignal, dispatch_walltime(NULL, DISPATCH_TIME_FOREVER));

 dispatch_release(ds);
 dispatch_release(dq);
 dispatch_release(exitsignal);

return 0;
}

client.m

#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>

#define PORT "2305" // the port client will be connecting to

#define MAXDATASIZE 100 // max number of bytes we can get at once

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
 if (sa->sa_family == AF_INET) {
 return &(((struct sockaddr_in*)sa)->sin_addr);
 }

return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
 int sockfd;
 struct addrinfo hints, *servinfo, *p;
 int rv;
 char s[INET6_ADDRSTRLEN];

if (argc != 2) {
 fprintf(stderr,"usage: client hostname\n");
 exit(1);
 }

memset(&hints, 0, sizeof hints);
 hints.ai_family = AF_UNSPEC;
 hints.ai_socktype = SOCK_STREAM;

if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
 return 1;
 }

//loop to obtain a socket
 for(p = servinfo; p != NULL; p = p->ai_next) {
 if ((sockfd = socket(p->ai_family, p->ai_socktype,
 p->ai_protocol)) == -1) {
 perror("client: socket");
 continue;
 }

if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
 close(sockfd);
 perror("client: connect");
 continue;
 }

break;
 }

if (p == NULL) {
 fprintf(stderr, "client: failed to connect\n");
 return 2;
 }

inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
 s, sizeof s);
 printf("client: connecting to %s\n", s);

freeaddrinfo(servinfo);

//use a libdispatch queue to read data from the socket
 dispatch_semaphore_t exitsignal = dispatch_semaphore_create(0);
 dispatch_queue_t dq = dispatch_queue_create("data", NULL);
 dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, dq);

dispatch_source_set_event_handler(ds, ^{
 int numbytes;
 char buf[MAXDATASIZE];

 int rfd = (int)dispatch_source_get_handle(ds);
 size_t bytesAvail = dispatch_source_get_data(ds);
 if (bytesAvail == 0) {
 dispatch_source_cancel(ds);
 return;
 } else {
 printf("..dispatch source %d has %lu bytes available.\n",rfd, bytesAvail);
 }

if ((numbytes = recv(rfd, buf, MAXDATASIZE-1, 0)) == -1) {
 perror("recv");
 exit(1);
 }
 buf[numbytes] = '\0';
 printf("client: received '%s'\n",buf);
 });

dispatch_source_set_cancel_handler(ds, ^{
 //handler to close the socket - then signal to exit
 int rfd = (int)dispatch_source_get_handle(ds);
 close(rfd);
 dispatch_semaphore_signal(exitsignal);
 });

dispatch_resume(ds);
 // block until the semaphore is signalled - when socket closes
 // second parameter is a timeout - nanoseconds
 dispatch_semaphore_wait(exitsignal, dispatch_walltime(NULL, 5*1000000000));

dispatch_release(ds);
 dispatch_release(dq);
 dispatch_release(exitsignal);

return 0;
}

GNU Smalltalk with C callouts #2

This is part 2 of a tutorial on C Callouts from GNU Smalltalk.

Part 1 showed how to compile a library and test program, and then call the library from GNU Smalltalk. In this post, we’re going to add more data types.

A full listing of all code is included at the bottom of this post

String arguments

The C library code, header and function:-

extern char* echoStr(char *str);
...
char* echoStr(char *str) {
printf("\n Inside echoStr: %s\n",str);
return (str);
}

And here are relevant snippets of the Smalltalk script. You can refer to the previous post and to the full listing at the bottom of the page to get the class declaration.

LibCall class >> echoString: str [
   <cCall: 'echoStr' returning: #string args: #(#string)> ]
...
Transcript showCr: (LibCall echoString: 'Yo. Off to see the c').

As you would expect, very similar to the addTen example of the previous post. The callout library is doing the heavy lifting to automatically convert C strings (pointers to null terminated char arrays) to Smalltalk strings.

A function call with two arguments

And lets look at an example with two parameters in the C function call. The example is a function that compares the length of two strings, and returns the length of the longest.

extern unsigned int longestStr(char *str1, char *str2);
unsigned int longestStr(char *str1, char *str2) {
    printf("\n Inside copyString: %s to %s\n",inStr, outStr);
    return ((strlen(str1) > strlen(str2)) ? strlen(str1) : strlen(str2));
}

And the smalltalk script

LibCall class >> longestString: source to: dest [
   <cCall: 'longestStr' returning: #uint args: #(#string #string)> 
]
...
length := LibCall lenStr: str1.
Transcript show: 'Length of %1 is: %2' % {str1. length asString}; nl.

Passing structs

GNU Smalltalk has a CStruct type for declaring C structs in Smalltalk. Here is the C struct we’re going to work with:-

struct      Record {
    int     id;
    char*   memo;     
    long    modified; 
}

And here are two C functions making use of the “Record” struct. A function to return a new person “Record”, and a function to change the text of the Record memo.

//function headers
extern struct Record* newBlankRecord(unsigned int *resultCode);
extern unsigned int replaceMemo(struct Record *rec, char* memo);

struct Record* newBlankRecord(unsigned int *resultCode) {
  //return a pointer to a new blank person Record
  struct Record *aRec = malloc(sizeof(struct Record));
  printf("\n Inside newRecord: Size in bytes allocated to struct 'Record': %lu\n",sizeof(sizeof(struct Record)));
  aRec->id = 0;
  aRec->memo = NULL;
  aRec->modified = 0;
  *resultCode = 0; //0 indicates success, error handling omitted
  return (aRec);  
}

unsigned int replaceMemo(struct Record *rec, char* memo) {
    //update the Record memo 
    char* recordMemo;
    recordMemo = (char*)malloc(strlen(memo));
    if (rec->memo != NULL) {
        free(rec->memo);
    }
    strcpy(recordMemo, memo);
    rec->memo = recordMemo;
    printf("\n Inside recMemo. Memo is '%s' \n",rec->memo);
    return (strlen(rec->memo));
}

And now the Smalltalk side. First a declaration of a structure in Smalltalk that will map to the C structure, and then two Smalltalk methods to access the two C function calls above. Note the declaration of the CRecord structure inside the Smalltalk class. Doing this enables us to pass a reference to the class (#self) in each place that a reference to the C structure is required.

CStruct subclass: CRecord [
         <declaration: #( (#id  #int)
                          (#memo #string)
                          (#modified #long))>
                      
CRecord class >> newBlankRecord: record [
   "class method to create a new #CRecord"
   "(#ptr #uint) is a single argument, an unsigned int pointer"
   <cCall: 'newBlankRecord' returning: #{CRecord} args: #((#ptr #uint))> 
]                     
replaceMemo: aMemo [
   "instance method to update the Record memo"
   <cCall: 'replaceMemo' returning: #uint args: #(#self #string)> 
]         
] "CRecord"

And here is the Smalltalk code to call the two functions in CRecord. As you can see the call to “replaceMemo” looks like standard Smalltalk on the right hand side even though there is a call to a C function implementing this code behind the scenes. In fact, the replaceMemo method could be wrapped in another method to check if retVal is non-zero, and if so, an exception would be thrown.

"newBlankRecord argument maps to an unsigned int pointer"
record := CRecord newBlankRecord: (CUInt value:10).
retVal := record replaceMemo: 'Lives at Bedrock'.

Now you may wondering about the #self in mapping from Smalltalk to C, and, is this the way it has to be done. In fact, the CStruct could have been declared in another class instead of in CRecord. For instance in the Smalltalk snippet below, I’ve separated the CStruct declaration from the function declarations; they are in separate classes. (There is no working example for this, but rather just to show how it could have been done). As you can see, the use of #self enables a nicer Smalltalk mapping of the C library.

CStruct subclass: CRecord [
         <declaration: #( (#id  #int)
                          (#memo #string)
                          (#modified #long))>
]                      

Cobject subclass: CRecordLib
CRecord class >> newBlankRecord: record [ 
   <cCall: 'newBlankRecord' returning: #{CRecord} args: #((#ptr #uint))> 
]                     
CRecordLib class >> replaceMemo: aMemo [
   <cCall: 'replaceMemo' returning: #uint args: #(#{CRecord} #string)> 
]         
] "CRecord"

And to wrap it up, here is a real world example, a post showing how to access the LDAP API from GNU Smalltalk.

http://lists.gnu.org/archive/html/help-smalltalk/2009-01/msg00112.html

Code listing below for the examples in this tutorial…

ccall2.h

struct       Record {
    int         id;
    char*       memo;     //variable length
    long        modified; //timestamp
};

extern unsigned int add(unsigned int a, unsigned int b);
extern unsigned int lenStr(char *str);
extern char*        echoStr(char *str);
extern unsigned int longestStr(char *str1, char *str2);
extern unsigned int replaceMemo(struct Record *record, char* memo);
extern unsigned int initializeRecord(struct Record *record);
extern struct Record* newBlankRecord(unsigned int *resultCode);

ccall2.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ccall2.h"

unsigned int add(unsigned int a, unsigned int b) {
    printf("\n Inside add()\n");
    return (a+b);
}

unsigned int lenStr(char *str) {
    printf("\n Inside lenStr: %s\n",str);
    return (strlen(str));
}

char* echoStr(char *str) {
    printf("\n Inside echoStr: %s\n",str);
    return (str);
}

unsigned int longestStr(char *str1, char *str2) {
    printf("\n Inside longestString: '%s' and '%s'\n",str1,str2);
    return ((strlen(str1) > strlen(str2)) ? strlen(str1) : strlen(str2));
}

struct Record* newBlankRecord(unsigned int *resultCode) {
  struct Record *rec =  malloc(sizeof(struct Record));
  printf(" Inside newBlankRecord: Size in bytes allocated to struct 'Record': %lu\n",sizeof(sizeof(struct Record)));
  rec->id = 90;
  rec->memo = NULL;
  rec->modified = 90;  
  *resultCode = 0;  //error handling omitted for clarity
  return (rec);  
}
 
unsigned int replaceMemo(struct Record *rec, char* memo) {
    char* recordMemo;
    recordMemo = (char*)malloc(strlen(memo));
    if (rec->memo != NULL) {
        free(rec->memo);
    }
    strcpy(recordMemo, memo);
    rec->memo = recordMemo;
    printf("\n Inside recMemo. Memo is '%s' \n",rec->memo);    
    return (strlen(rec->memo));
}

unsigned int initializeRecord(struct Record *rec) {
  rec->id =100;
  rec->memo = NULL;
  rec->modified = 100; 
  return (0); //error handling omitted for clarity 
}

ccall2test.c – the test program

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ccall2.h"

int main(void)
{
    unsigned int a = 1;
    unsigned int b = 2;
    unsigned int result = 0;
    char string1[] = "hello";
    char *string2  = "universe";    

    result = add(a,b);
    printf("The result is [%u]\n",result);

    result = lenStr(string1);
    printf("Length of '%s' is [%u]\n",string1,result);

    printf("Echo '%s' is '%s'\n",string1,echoStr(string1));

    result = longestStr(string1, string2);
    printf("longestStr: Longest of '%s' and '%s' has length [%u]\n\n",string1, string2, result);
    
    unsigned int retVal2 = 9999;
    struct Record *rec2 = newBlankRecord(&retVal2);
    printf("newBlankRecord: id is [%u] with resultCode: [%u]\n\n",rec2->id, retVal2);

    struct Record *rec =  malloc(sizeof(struct Record));
    int retVal = initializeRecord( rec);
    printf("initializeRecord: id is [%u]\n",rec->id);

    char memo[] = "Lives at Bedrock";
    result = replaceMemo(rec, memo);
    printf("replaceMemo: id is [%u]\n", rec->id);
    printf("Result from replaceMemo is the length of the memo: [%u]\n",result);      
    
    return 0;
}

ccall2.st

#!/usr/local/bin/gst -f
"call a C library"

CObject subclass: LibCall [

LibCall class >> add: num1 with: num2 [
   <cCall: 'add' returning: #uint args: #(#uint #uint)> 
]
LibCall class >> lenStr: str [
   <cCall: 'lenStr' returning: #uint args: #(#string)> 
]
LibCall class >> echoString: str [
   <cCall: 'echoStr' returning: #string args: #(#string)> 
]
LibCall class >> longestString: source to: dest [
   <cCall: 'longestStr' returning: #uint args: #(#string #string)> 
]

] "LibCall"


CStruct subclass: CRecord [
         <declaration: #( (#id  #int)
                          (#memo #string)
                          (#modified #long))>
                          
   CRecord class >> newBlankRecord: retVal [
      "(#ptr #uint) is a single argument, an unsigned int pointer"
      <cCall: 'newBlankRecord' returning: #{CRecord} args: #((#ptr #uint))>
   ]
   initializeRecord: aRecord [
      <cCall: 'initializeRecord' returning: #uint args: #(#self)>
   ]
   replaceMemo: aMemo [
      <cCall: 'replaceMemo' returning: #uint args: #(#self #string)>
   ]        

] "CRecord"



"mainline --------------------------------"
| sum length str1 str2 retVal record |

DLD addLibrary: 'libccall2'.

str1 := 'hello'.
str2 := 'universe'.

sum := LibCall add: 1 with: 1.
Transcript showCr: ('Sum is: %1' bindWith: (sum asString)).

length := LibCall lenStr: str1.
Transcript show: 'Length of %1 is: %2' % {str1. length asString}; nl.

Transcript show: 'Echo %1 is %2' % {str1. (LibCall echoString: str1)}; nl.

length := LibCall longestString: str1 to: str2.
Transcript show: 'Longest of ''%1'' and ''%2'' has length [%3]' % {str1. str2. length printString}; nl.

retVal := CUInt value:10. "for C function requiring pointer to unsigned int"
record := CRecord newBlankRecord: retVal.
Transcript showCr: 'newBlankRecord: id is [%1]' % { record id value asString}.

record := CRecord new.
retVal := record initializeRecord: record.
Transcript showCr: 'initializeRecord: id is [%1]' % { record id value asString}.

retVal := record replaceMemo: 'Lives at Bedrock'.
Transcript showCr: 'replaceMemo: id is [%1] with memo ''%2''' % {record id value asString. record memo value asString}.

Makefile

CC=gcc

all: libccall2.so ccall2test.o
       $(CC) -o ccall2test ccall2test.o -L. -lccall2

libccall2.so: ccall2.c
       $(CC) -fPIC -c ccall2.c -o  ccall2.o
       $(CC) -shared -Wl,-soname,libccall2.so -o libccall2.so ccall2.o

clean:
       rm *.o *.so
Link

GNUstep, blocks, GCD install on PCBSD 9.1

Here is the recipe I used to install GNUstep into a ports jail in PCBSD 9.1.

The process below is required because FreeBSD GNUstep packages are currently compiled with GCC. It is expected that by the time FreeBSD 10 and PCBSD 9.1 are released, all the GNUstep packages will be compiled with clang. That being the case, this guide won’t be required, and GNUstep with GCD with blocks will just work out of the box.

(A previous post has more detail).

Create a new jail in PCBSD 9.1, include ports

portsnap fetch update
#vipw  ->
#  make /usr/local/bin/bash the shell of the "toor" account
#  add new account "devel" also with bash shell
#passwd toor -> set root passwd
#su - toor   	# need to install sudo...


pgk_add -r vim bash portmaster subversion libdispatch
cd /usr/ports

portmaster /usr/ports/lang/clang33

# gnustep sources
mkdir /usr/local/src
svn co svn://svn.gna.org/svn/gnustep/modules/ gnustep

# check what packages could be required to install gnustep...
pkg_add -n -r gnustep-base gnustep-back
#(showed following packages required: libsndfile flac libao libvorbis
#  libogg aspell libxslt libgcrypt gnutls libpg libGLU libGL libdrm
#  libpciaccess giflib libXmu p11-kit mDNSResponder gmp nettle mpfr
#  libgpg libgcrypt xf86vidmodeproto dri2proto pciids libXxf86vm

# install recommended packages prior compilation
portmaster textproc/libxml      # pulled in ports for libgpg libgcrypt
				#pkgconf libxml2 libxslt
portmaster devel/icu
portmaster devel/cmake		# pulled in port for cmake-modules


export CC=/usr/local/bin/clang33
export CXX=/usr/local/bin/clang++33
# gnustep-make; required for libobc2 build
cd /usr/local/src/gnustep/core/make/
./configure
gmake
gmake install
. /usr/local/share/GNUstep/Makefiles/GNUstep.sh

# build libobjc2
cd ../../dev-libs/libobjc2
mkdir Build
cd Build
cmake .. -DCMAKE_C_COMPILER=/usr/local/bin/clang33 -DCMAKE_CXX_COMPILER=/usr/local/bin/clang++33
make
make install

#find / -name libobjc.so.*
#  /usr/local/lib/libobjc.so.4.6
#ldd /usr/local/lib/libobjc.so
#  /usr/local/lib/libobjc.so:
#        libthr.so.3 => /lib/libthr.so.3 (0x801235000)
#        libdispatch.so.0 => /usr/local/lib/libdispatch.so.0 (0x801458000)
#        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x80166a000)
#        libm.so.5 => /lib/libm.so.5 (0x80197a000)
#        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x801b9b000)
#        libc.so.7 => /lib/libc.so.7 (0x80081b000)
#        libBlocksRuntime.so.0 => /usr/lib/libBlocksRuntime.so.0 (0x801da8000)

# install gnustep-make again
./configure
gmake
gmake install
# source GNUstep again
. /usr/local/share/GNUstep/Makefiles/GNUstep.sh

# gnustep-base
cd ../base
./configure
gmake
gmake install

# gnustep-gui
# Note: all of "tiff jpeg png giflib aspell" packages are already installed
cd ../gui
./configure
gmake
gmake install

# gnustep-back
# Note: cairo already installed, but libGL is not.
cd ../back
./configure
gmake
gmake install

GNU Smalltalk with C Callouts

Chances are that if you’re using GNU Smalltalk you’re going to want to call out to a C program or some C libraries.

First off, the docs are here: http://www.gnu.org/software/smalltalk/manual/html_node/C-and-Smalltalk.html#C-and-Smalltalk. For C callouts, there are two links which are most helpful, being “C Callout” and “C Data Types”. The rest of the links are mostly related to going the other way, i.e. calling from C into Smalltalk.

This post is an introductory example of how to access a shared library from Smalltalk. To get a feel for what is going on, we’ll make our own shared library and then call it from Smalltalk. There are four files to copy as follows:

  • a shared library with a single numeric function that adds 10 to an argument and returns the sum (tutcall1.h and tutcall1.c)
  • a C test program to use the library (tutcall1test.c)
  • a GNU Smalltalk script to use the library (tutcall.st)

—> tutccall1.h


extern unsigned int addTen(unsigned int aNum);

—> tutccall1.c

#include
#include "tutccall1.h"
unsigned int addTen(unsigned int aNum) {
printf("\n Inside addTen()\n");
return (aNum+10);
}

—> tutccall1test.c

#include
#include "tutccall1.h"

int main(void)
{
unsigned int aNum = 5;
unsigned int result = 0;
result = addTen(aNum);
printf("Test addTen: Passed [%u] and the result is [%u]\n",aNum,result);
return 0;
}

—> tutccall1.st

#!/usr/bin/gst -f    "set to the path of your gst binary"

"Simple call from GNU Smalltalk to a C library"
CObject subclass: ClassCall [
"demonstrating a class method callout"

ClassCall class >> addTen: aNum [
  <cCall: 'addTen' returning: #uint args: #(#uint)>
] ]

CObject subclass: InstanceCall [
"To demonstrate an instance method callout."
addTen: aNum [
  <cCall: 'addTen' returning: #uint args: #(#uint)>
] ]

"mainline"
| result callout |
Transcript showCr: '.started'.

"Link GNU Smalltalk with the Library we created"
DLD addLibrary: 'libtutccall1'.

"Callout using a class method"
result := ClassCall addTen: 5.
Transcript show: 'ClassCall: Result is: %1' % {result asString}; nl.
Transcript show: 'Result: '. result inspect.
Transcript showCr: '---'.

"Callout again, but this time using an instance method"
callout := InstanceCall new.
result := callout addTen: 5. "or, InstanceCall new addTen: 5."
Transcript show: 'InstanceCall: Result is: %1' % {result asString}; nl.
Transcript showCr: ''.

Compilation

To keep things simple, I put all the files in the same directory, say, /opt/gst/ccall1. Set the environment variable LD_LIBRARY_PATH so that programs can find the shared library:-

export LD_LIBRARY_PATH=/opt/gst/ccall1

Here is a Makefile to build the shared library and test program.

CC=gcc

all: libtutccall1.so tutccall1test.o
        $(CC) -o tutccall1test tutccall1test.o -L. -ltutccall1 

libtutccall1.so: tutccall1.c
        $(CC) -fPIC -c tutccall1.c -o  tutccall1.o
        $(CC) -shared -Wl,-soname,libtutccall1.so -o libtutccall1.so tutccall1.o

clean:
        rm *.o *.so

“make” and you should find it has created the shared library “libtutccall1.so”, as well as a test executable “tutccall1test”.

Testing the Library

Run the tutccall1test program to check that the library is working. You should get the following output

 Inside addTen()
Test addTen: Passed [5] and the result is [15]

If ever the test program shows an error on loading the shared library, use the ldd utility to check that the library is available. For example:

$> ldd ./tutccall1test
linux-vdso.so.1 =>  (0x00007fff35fff000)
libtutccall1.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcfe8728000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcfe8ac5000)

And set the LD_LIBRARY_PATH environment variable (as mentioned earlier)

$> export LD_LIBRARY_PATH=/opt/gst/libs/
$> ldd ./tutccall1test
linux-vdso.so.1 =>  (0x00007fff2e7ff000)
  libtutccall1.so => /opt/gst/libs/libtutccall1.so (0x00007fc6c83c3000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc6c8028000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc6c85c6000)

$> ./tutccall1test
Inside addTen()
Test addTen: Passed [5] and the result is [15]

Running the Smalltalk script

And here is the output you can expect.

$> ./tutccall1.st

 Inside addTen()
ClassCall: Result is: 15
Result: An instance of SmallInteger

---

 Inside addTen()
InstanceCall: Result is: 15

Regarding the Smalltalk script:-

  • the DLD command references the library we created, i.e. libtutccall1.so, omitting the “.so” suffix.
  • cCall is used to define a C function. As you can see, the Smalltalk method name doesn’t have to be the same as the C function name although it is good practice to keep them similar.
  • The C function addTen accepts and returns an unsigned int, which is mapped to #uint. Available C return types and argument types are listed on this page: http://www.gnu.org/software/smalltalk/manual/html_node/C-callout.html#C-callout

GNUstep install with clang, blocks, and Grand Central Dispatch (GCD)

Grand Central Dispatch is an Apple  technology to assist building and running apps on multi-core processors. The library for Grand Central Dispatch is called libdispatch, and tasks can be submitted to libdispatch with blocks, or with a C API.

Here is a record of my installation of GNUstep-base with clang, blocks and Grand Central Dispatch (GCD) on Linux.

GNUstep runtime – libobjc2

The runtime library for Objective C code started out as libobjc until Apple released Objective C version 2, when it became libobjc2. You can get the background here: http://wiki.gnustep.org/index.php/ObjC2_FAQ .

For Objective C version 2 support you have a choice, in that you can use either a recent GCC, or clang. However, keep in mind that GCC at time of writing doesn’t support blocks. So while libobjc2 can be compiled with gcc (since the blocks runtime in libobjc2 doesn’t use blocks), you must compile your own code with clang.  Nevertheless, it is recommended to build libobjc2 with a recent clang.

Apart from blocks, the runtime should support the same features irrespective of  what compiler it is built with.

As you may know GNUstep-base provides block support, irrespective of compiler support for blocks. However in practice, I’m told that GNUstep-base should be built with clang for use with blocks (David Chisnall of GNUstep advises the GNUstep developers made an effort to avoid this, but it might not quite work, and in particular the subclassing of NSRegularExpression won’t work correctly if GNUstep is not built with blocks support).

Also, I found that at the moment (June 2013), libdispatch requires clang to build it.

So one thing is clear, with a stated goal of running GNUstep with blocks and libdispatch, we’re going to need a libobjc2 compiled with clang.

Distributions

I tend to use Debian server side so I had a look at the packages available. It turns out that Debian wheezy and Ubuntu 13 build libobjc2 and libdispatch with clang, while GNUstep packages are still built with gcc.

And if your flavour is FreeBSD, FreeBSD and PCBSD also build libobjc2 and libdispatch with clang, and GNUstep with gcc.

If you do install libdispatch on any of these systems you’ll see a package called cblocksruntime is installed also. What is cblocksruntime? It is an implementation of C blocks to enable compilation and running of libdispatch without the need for libobjc2. Now the way libblocksruntime is built, it will defer to libobjc2 if libobjc2 is linked. So in fact, we’re fine to have libobjc2 and libblocksruntime installed on the same system and if this is the case, the blocks support in libobjc2 will take precedence over that in cblocksruntime.

First attempt…

Observing that libobjc2 and libdispatch are built with clang while gnustep-base is built with GCC, I wasn’t sure if everyone would get along fine or if it was going to turn into a family squabble. It turned out to be a bit of both.

It turns out that that the objc.h header which is included in Foundation.h should come from libobjc2. In fact, as it stands right now, Debian and Ubuntu provide objc.h from GCC (Note: FreeBSD 9.x and PCBSD will be fine here unless you install GCC from ports). So once I started writing code on Ubuntu, I had to install GCC and put the location of objc.h in <GNUstep root>/Makefiles/config.make. At the same time I was trying to find my way with GNUstep and I ran into other issues. In addition, posts to the gnustep-discuss newsgroup this year consistently recommended installing from source, so to the the source I went.

The rest of this post will describe how to do a complete installation from source of clang, libobjc2, gnustep-base, and libdispatch.

I’ve since found that there is another way – isn’t there always. My understanding is that one can use clang as shipped on Debian or FreeBSD, to compile libobjc2 and gnustep-base, and the existing libdispatch will play nicely. If you do this, David Chisnall provides the following comments,

  • The objc.h header should be provided by libobjc2.  If you’re installing on Linux and don’t have GNUstep installed, then you may have problems because the default install location for the headers will be in /usr/local/include/objc, which won’t be in your search path.  Changing the prefix to /usr/ (from /usr/local) should fix this.
  • On FreeBSD / PC-BSD, you can just install the libdispatch port and add -ldispatch to your linker flags.  This version works out of the box with GNUstep (and doesn’t have any GNUstep-specific patches applied, so others from the same upstream should as well).
  •  It is completely safe to compile and link libdispatch against LLVM’s libBlocksRuntime and then link your code against libobjc2.  The weak symbols in libBlocksRuntime will be ignored in preference to the ones in libobjc2.

libobjc2 and gnustep-base installation

I installed and compiled clang, libobjc2, and gnustep-base on each of Gentoo, Kubuntu 13. Here is a look at each platform.

Gentoo

Gentoo takes a while to build the OS initially, but was the simplest to get everything working. It was pretty much just a case of setting some use flags and then choosing all the packages I wanted. The only additional step was a perusal of the clang howto, and I chose to stay with gcc for the system, and use clang for all the GNUstep related builds. (see http://wiki.gentoo.org/wiki/Clang)

See the end of this post for the Gentoo configuration files I used.

Kubuntu

I recommend a fresh Debian or Ubuntu installation. If not, remove all gnustep packages, libobjc* packages, libdispatch* packages, and libblocksruntime* packages.

Ivan Vucica has a handy install script for download: https://bitbucket.org/ivucica/gnustep-ubuntu/src .  As well as the packages referenced in the script, you will also need cmake; which you should install now. Re Ivan’s script, here are a couple of things:-

  • kubuntu 13 does not have the openssl-dev package so left it out
  • libobjc2 now requires cmake instead of GNU make. Check out the install instructions in the file INSTALL inside the libobjc2 source and build accordingly. When you run “cmake ..”, look at the output and make sure that
    C compiler is clang, and,
    CXX compiler is clang++

PCBSD 9.1

I had a look at PCBSD because it is based on FreeBSD, and of course OS X is derived from FreeBSD. As a result, libdispatch on FreeBSD and PCBSD uses the kernel interfaces for event dispatch directly, it doesn’t need an emulation wrapper.

I haven’t run PCBSD before and was impressed. PCBSD has a nifty feature called the warden to manage containers implementing a ports jail. I used the warden to set up a container with its own IP address, and a set of FreeBSD packages implementing GNUstep and libdispatch. This package container is completely independant of packages in the main PCBSD package repository, and I can see that you could set up a developer container and a test-deployment container on the same system. Such a setup is way simpler and uses less machine resource than maintaining two separate virtual machines to do the equivalent.

It turned out that the software stack of  libobjc2, gnustep-base, and libdispatch was implemented in a similar way on FreeBSD as on Debian. Namely, libobjc2 and libdispatch compiled with clang, and GNUstep libraries and applications compiled with GCC. By the way, a change of compiler to clang, for all the GNUstep software is imminent. This post describes the install of GNUstep, blocks and GCD on PCBSD 9.1

Try it out… GNUstep code with Blocks

Being new to GNUstep, I quickly learnt the importance of the GNUstep.sh script. GNUstep.sh sets up a number of environment variables which are absolutely necessary to compile and run GNUstep code. On Gentoo in a bash shell run

“. /usr/share/GNUstep/Makefiles/GNUstep.sh”, and put this line in your profile at startup by adding it to ~/.bashrc . That done you’re ready for the code below.

Note: On *BSD, GNUstep.sh is under /usr/local rather than /usr.

Below is a sample program with blocks called source.m, and a make file called GNUmakefile. Put the two files “source.m” and “GNUmakefile” in the same directory and type “gmake” or “make” (whichever calls “GNU make” on your system). You should then see a directory appear called “obj” and in it should be the program “blocks” which you can run.

Program: source.m

#include

int main (int argc, char ** argv) {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 NSDate *date = [NSDate date];
 NSLog(@"Begin: The date and time is %@", date);

void (^now)(void) = ^ {
 NSLog(@"Block: The date and time is %@", date);
 };

sleep(5);
 date = [NSDate date];
 NSLog(@"After delay: The date and time is %@", date);
 now();

[pool release];
 return 1;
}

makefile: GNUmakefile

include $(GNUSTEP_MAKEFILES)/common.make

ADDITIONAL_OBJCFLAGS += -fblocks -fobjc-runtime=gnustep-1.7
TOOL_NAME = blocks
blocks_OBJC_FILES = source.m

include $(GNUSTEP_MAKEFILES)/tool.make

Output:

Begin: The date and time is 2013-06-04 09:19:39 +1200
After delay: The date and time is 2013-06-04 09:19:44 +1200
Block: The date and time is 2013-06-04 09:19:39 +1200

Blocks and Grand Central Dispatch

Here is some background on GCD and blocks

  • GCD: As far as I can make out, there are two source repositories for an implementation of GCD on Linux. One by [https://www.heily.com/trac/libdispatch/ Mark Heily] and one called [http://opensource.mlba-team.de/xdispatch/docs/current/index.html libxdispatch]. So far as using GCD with GNUstep, Mark’s library is the one to use, and this is also the libdispatch library in Debian and Ubuntu.
  • A Grand Central Dispatch implementation requires libpthread and libkqueue.
  • libpthread is the POSIX threading library. There are some non-standard extensions for allowing the kernel to manage work queues based on system load, but on non-Apple platforms they’re just emulated in userspace (unless you apply Stacy Son’s patches to FreeBSD).
  • libkqueue is a userspace implementation of BSD’s kqueue() kernel event notification mechanism. On Linux for instance, libkqueue translates epoll, inotify, signalfd, and timderfd, to the kevent structure.
  • Blocks: Using blocks with GCD will often result in simpler and more elegant code, but blocks are not part of standard C. GNUstep implements exactly the same ABI for blocks as Apple / LLVM’s libBlocksRuntime, it just implements the integration between blocks and the Objective-C runtime differently.  It is completely safe to compile and link libdispatch against LLVM’s libBlocksRuntime and then link your code against libobjc2.  The weak symbols in libBlocksRuntime will be ignored in preference to the ones in libobjc2.

To install libdispatch I used a repository from Nick Hutchinson which in turn is based on the library of Mark Heily. I used Nick’s repository because it has recent patches while Mark’s hasn’t been updated for a while.

Download each of:

The installs for libpthread and libkqueue are gnustep-make builds so check you have GNUstep environment variables in your current shell (“export  | grep GNU” in a bash shell).

Check the INSTALL files, but as I remember for libpthread_workqueue and libkqueue, installation just involved:-

./configure
make install

Each library also has a test created either in the top level directory or in the tests directory. Run the tests. You may need to “make check” to build/run the test.

And compile and install libdispatch. libdispatch has a separate build style than the others:

sh autogen.sh
./configure
make
make install

I got the following error on Gentoo Linux, when running configure

checking for sys/event.h... no

checking for KQUEUE... configure: error: Package requirements (libkqueue) were n

Package libkqueue was not found in the pkg-config search path.
Perhaps you should add the directory containing `libkqueue.pc'
to the PKG_CONFIG_PATH environment variable

Fixed with:

mv /usr/local/lib64/pkgconfig/libkqueue.pc \
  /usr/share/pkgconfig/

And the next problem was related to the i18n library which has “block” declared in the header like so…

/usr/include/unistd.h - line 1149
extern void encrypt (char *__block, \
   int __edflag) __THROW __nonnull ((1));

I commented out line 1149 to get the compile completed, and then uncommented afterwards.

And at the end of all of this a nice new libdispatch library.

Time to try it out. Below is the source for a simple program to call Grand Central Dispatch, along with a GNUMakefile.

#include
#include

int main (int argc, char ** argv)
{
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 dispatch_async(myQueue, ^{
 printf("this is a dispatch block!\n");
 });
 dispatch_async(myQueue, ^{
 printf("this is another dispatch block!\n");
 });
 sleep(1);
 [pool release];
 return 1;
}

GNUmakefile:

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = gcdtest

gcdtest_LIBRARIES_DEPEND_UPON += -ldispatch -lkqueue -lpthread_workqueue

gcdtest_OBJC_FILES += source.m

# Additional library directories the linker should search
ADDITIONAL_LIB_DIRS += -L/usr/local/lib64
# Additional TOOL libraries to link
ADDITIONAL_TOOL_LIBS += -ldispatch -lkqueue -lpthread_workqueue

include $(GNUSTEP_MAKEFILES)/tool.make

One last thing – memory management

If you’ve built GNUstep with clang as described above, i.e. a version of clang supporting blocks, as on Debian or later, you have choices for memory management as on OS X:-

  • Traditional autorelease pools
  • ARC and autorelease pool blocks (autoreleasepool{})
  • Garbage Collection. Keeping in mind that garbage collection has been deprecated in OS X as of 10.8. If you wish to implement garbage collection, compile with boehm-gc as per this wiki article. http://wiki.gnustep.org/index.php/Building_GNUstep_with_Clang

Credits: David Chisnall for a lot of input into this post to ensure technical accuracy

Any errors remaining are of course all mine.

————————————————————-

Funtoo/Gentoo config files below

$ cat /etc/portage/env/clang
CC=clang
CXX=clang++

CFLAGS="-march=core2 -O2 -pipe "
#CFLAGS="-march=core2 -O2 -pipe -fblocks "
#CFLAGS="-march=core2 -O2 -pipe -pthread -fblocks"

# from clang on funtoo docs http://www.funtoo.org/wiki/Clang
#CFLAGS="-O2 -march=native -mtune=native -pipe"
#CXXFLAGS="-O2 -march=native -mtune=native -pipe"
#LDFLAGS="-Wl,--as-needed"

$ cat /etc/portage/package.env
gnustep-base/libobjc2 clang
dev-libs/libatomic_ops clang
dev-libs/nettle clang
gnustep-base/gnustep-make clang
dev-libs/libtasn1 clang
net-libs/gnutls clang
gnustep-base/gnustep-base clang
app-arch/libarchive clang
dev-util/cmake clang
dev-libs/compiler-rt clang
gnustep-base/gnustep-gui clang
gnustep-base/gnustep-back-cairo clang
virtual/gnustep-back clang
gnustep-apps/projectcenter clang
# email
gnustep-apps/gnumail clang
gnustep-apps/addresses clang
gnustep-libs/pantomime clang

$ cat /etc/portage/make.conf
CFLAGS="-march=core2 -O2 -pipe"
CXXFLAGS="-march=core2 -O2 -pipe"
USE="-minimal libobjc2 ldap native-exceptions jpeg libnotify lock session startup-notification svg thunar "$ eselect profile listCurrently available arch profiles:
 [1] funtoo/1.0/linux-gnu/arch/x86-64bit *
 [2] funtoo/1.0/linux-gnu/arch/x86-64bit/pure64
Currently available build profiles:
 [3] funtoo/1.0/linux-gnu/build/stable
 [4] funtoo/1.0/linux-gnu/build/current *
 [5] funtoo/1.0/linux-gnu/build/experimental
Currently available flavor profiles:
 [6] funtoo/1.0/linux-gnu/flavor/minimal
 [7] funtoo/1.0/linux-gnu/flavor/core *
 [8] funtoo/1.0/linux-gnu/flavor/desktop
 [9] funtoo/1.0/linux-gnu/flavor/workstation
Currently available mix-ins profiles:
 [10] funtoo/1.0/linux-gnu/mix-ins/audio
 [11] funtoo/1.0/linux-gnu/mix-ins/console-extras
 [12] funtoo/1.0/linux-gnu/mix-ins/dvd
 [13] funtoo/1.0/linux-gnu/mix-ins/gnome
 [14] funtoo/1.0/linux-gnu/mix-ins/kde *
 [15] funtoo/1.0/linux-gnu/mix-ins/mate
 [16] funtoo/1.0/linux-gnu/mix-ins/media
 [17] funtoo/1.0/linux-gnu/mix-ins/print
 [18] funtoo/1.0/linux-gnu/mix-ins/python3-only
 [19] funtoo/1.0/linux-gnu/mix-ins/rhel5-compat
 [20] funtoo/1.0/linux-gnu/mix-ins/server-db
 [21] funtoo/1.0/linux-gnu/mix-ins/server-mail
 [22] funtoo/1.0/linux-gnu/mix-ins/server-web
 [23] funtoo/1.0/linux-gnu/mix-ins/X *
 [24] funtoo/1.0/linux-gnu/mix-ins/xfce
 [25] funtoo/1.0/linux-gnu/mix-ins/vmware-guest