Syzygy includes a portability layer which strives to make writing cross-platform Unix/Win32 applications as simple as possible. One Syzygy design goal is to ensure that as few platform specific #ifdef's as possible occur in layers of code above this one.

Network Sockets

The arSocket and arUDPSocket classes provide a portable sockets API. The arSocket class wraps the native TCP socket implementation on each platform, hiding the minor differences in function names or call signatures between Win32, Linux, Darwin, and Irix. When you create an arSocket object, you must specify whether it will be used to accept connections (AR_LISTENING_SOCKET) or transmit data (AR_STANDARD_SOCKET). For example:

arSocket* acceptSocket = new arSocket(AR_LISTENING_SOCKET);
arSocket* dataSocket = new arSocket(AR_STANDARD_SOCKET);

Accepting a new connection via a socket looks like this:

acceptSocket.ar_accept( &dataSocket );

Each arSocket object has an associated numerical ID that is set by the programmer. The intent is that a manager object should be able to use these IDs to manipulate a set of sockets.

Some socket options are also set via class methods:

bool arSocket::setReceiveBufferSize(int size)

Sets the size of the TCP receive buffer.


bool arSocket::setSendBufferSize(int size)

Sets the size of the TCP send buffer.

bool arSocket::smallPacketOptimize(bool flag)

Disable Nagle's Algorithm iff flag is "true". Many TCP implementations enable Nagle's Algorithm, which reduces the performance of real-time applications that send small packets relatively slowly.

bool arSocket::reuseAddress(bool flag)

Only makes sense for a listening socket. If set to "true", then the socket can bind to a previously bound address.

Serial Ports

Syzygy provides the arRS232Port class for uniform access to serial ports. A subset of the possible port parameters is supported, and the range of parameter values varies slightly between platforms.

Construct an arRS232Port without any arguments. To open it, call:

bool arRS232Port::ar_open( const int portNumber,
                            const unsigned long baudRate,
                            const unsigned int dataBits,
                            const float stopBits,
                            const string& parity );

Port numbers start with 1 on both platforms, i.e. under Linux /dev/ttyS0 is port 1. The function returns a bool indicating success or failure. Currently supported parameter values are:

To write to a serial port, use:

int arRS232Port::ar_write( const char* buf, const int numBytes );

which attempts to write numBytes bytes, or

int arRS232Port::ar_write( const char* buf );

which writes until a null character is reached. In either case, the function returns the number of bytes actually written or -1 on failure. Reading from a serial port is accomplished with:

int arRS232Port::ar_read( char* buf, const unsigned int numBytes );

On both platforms, this function will block until either a tenth of a second has passed or at least one character has been read. It repeats this step until either a user-specified timeout has been reached or numBytes bytes have been read. It returns the number of characters actually read. The timeout is set using:

bool arRS232Port::ar_setTimeout( const unsigned int timeout );

which takes a timeout value in tenths of a second and returns a bool indicating success or failure.

To flush any characters from the input and output buffers, use:

bool arRS232Port::ar_flushInput();
bool arRS232Port::ar_flushOutput();

To close the port, use:

bool arRS232Port::ar_close();

Threads

Threads are slightly different on Win32 and in the various Unix pthreads implementations. Syzygy has a common abstraction, arThread, that wraps the lowest-common denominator features.

You create and start a thread as follows:

arThread myThread;
void threadFunction( void* threadData ) { <thread task> }
void* threadData = <pointer to data you want the thread to access>
myThread.beginThread( threadFunction, threadData );

Note that this differs from pthreads, where threads are allowed to return void*.

Mutexes/Locks

There are two different but functionally-equivalent mutex classes. The newer, object-oriented arLock class is easier to use:

arLock myLock;
myLock.lock();                  // block until you get ownership.
bool isMine = myLock.tryLock(); // try to get ownership, but
                                // return immediately.
myLock.unlock();                // release the lock.

There is also the older arMutex class, which is used as follows:

arMutex myMutex;
ar_mutex_init( &myMutex );
ar_mutex_lock( &myMutex );
ar_mutex_unlock( &myMutex );

The main disadvantage is the requirement to call ar_mutex_init(); if you try to use an un-inited arMutex, your program will crash.

Condition Variables/Signals/Events

NOTE: This section is particularly weak. Re-write!!

Syzygy presents a lowest-common denominator abstraction for signals and condition variables.

Signals are implemented in the arSignalObject class. An arSignalObject enters a signalled state when its sendSignal() method is called. It remains in a signalled state until either its reset() or receiveSignal() method is called, at which time it returns to unsignalled. receiveSignal() blocks until another thread calls sendSignal().

Syzygy condition variables, as implemented by arConditionVar, work like pthreads condition variables except that only a single waiting thread can be awakened by a signal() call.

There is also an arThreadEvent class, based on the EVENT class of Walmsley, "Multi-threaded Programming in C++".

Time

Syzygy provides a uniform way to query the system time. It defines a time struct:

struct ar_timeval {
  int sec;
  int usec;  // microseconds
};

It also provides functions for querying the time and calculating the difference of two times:

struct ar_timeval ar_time();

Returns the current system time.

double ar_difftime( struct ar_timeval t2, struct ar_timeval t1 )

Returns the number of microseconds from t1 to t2.