The select() function call provides functionality
to scan descriptors for a ready event. The select()
call
requires the system to scan through a bit mask of a file descriptor set against
its list of open descriptors to check for ready events, only stopping when
it reaches a maximum descriptor value. For a system with a relatively small
number of open descriptors this is quite efficient but for a large amount
of descriptors can be overly burdensome.
The Symbian P.I.P.S. libraries
are not expected to be dealing with a very large number of file descriptors
and so select()
should be satisfactory for any applications
on a phone.
Note: P.I.P.S. does not support the poll()
function.
Monitoring two pipes using select()
The following code segment shows the usage of the select()
function:
Int main(int argc, char **argv) { FILE* ChildProcessStream1; FILE* ChildProcessStream2; int ChildProcessFD1; int ChildProcessFD2; fd_set SelectFDs; int MaxFD; struct timeval timeout; int Ret; //Create child process 1 using popen(). ChildProcessStream1 = popen("/root/PortDoc/Example7_c/Symbian/Child/ChildProg", "r"); if(ChildProcessStream1 == NULL) { printf("\n Failure to create child process 1 with popen()\n"); return EXIT_FAILURE; } //Create child process 2 using popen(). ChildProcessStream2 = popen("/root/PortDoc/Example7_c/Symbian/Child/ChildProg", "r"); if(ChildProcessStream2 == NULL) { printf("\n Failure to create child process 2 with popen()\n"); return EXIT_FAILURE; } //Convert streams to file descriptors ChildProcessFD1 = fileno(ChildProcessStream1); ChildProcessFD2 = fileno(ChildProcessStream2); //Setup the select set FD_ZERO(&SelectFDs); FD_SET(ChildProcessFD1, &SelectFDs); MaxFD = ChildProcessFD1; FD_SET(ChildProcessFD2, &SelectFDs); //Calculate the largest Descriptor if (ChildProcessFD2 > MaxFD) MaxFD = ChildProcessFD2; //Setup a time out value in case neither return in a sufficient time timeout.tv_sec = 3; timeout.tv_usec = 0; //Issue select request Ret = select(MaxFD + 1, &SelectFDs, NULL, NULL, &timeout); if (Ret == -1) { //Error occurred printf("\nCould not poll, error=%d",errno); return EXIT_FAILURE; } else if (Ret == 0) { //Timed out printf("\nTimed Out"); } else { //Create a receive buffer, and zero contents before receiving. Char RxBuffer[100]; memset(RxBuffer,0,sizeof(RxBuffer)); if (FD_ISSET(ChildProcessFD1, &SelectFDs)) { //Wait for data from child process 1. Child sends a string. Int nbytes = read(ChildProcessFD1,RxBuffer,sizeof(RxBuffer)); printf("\nMessage Received from Child1 First =%s",RxBuffer); } else if (FD_ISSET(ChildProcessFD2, &SelectFDs)) { //Wait for data from child process 2. Child sends a string. Int nbytes = read(ChildProcessFD2,RxBuffer,sizeof(RxBuffer)); printf("\nMessage Received from Child2 First =%s",RxBuffer); } } //Wait for Child Process to complete pclose(ChildProcessStream1); pclose(ChildProcessStream2); return EXIT_SUCCESS; }
The underlying implementation of sockets on the Symbian platform imposes some restrictions on the socket options available in P.I.P.S. and has also resulted in some new non-standard options to be created.
The
introduction of multi-homing (single link, multiple IP addresses) on Symbian
phones and the various methods of connecting to networks, such as Wi-Fi and
3G, with their varying degrees of cost to the user have made it important
to be able to choose which interface or access point is used to route socket
traffic. The Symbian RConnection
class provides this functionality,
and is exposed in P.I.P.S. using some extra IOCtl options.
The list of socket options available in P.I.P.S. are as follows:
Socket Option |
New |
Description |
SIOCADDRT |
Yes |
Adds an entry to the interface routing table using the parameters
in the |
SIOCATMARK |
No |
Determines whether the read pointer is currently pointing to the logical mark in the data stream, testing whether the next read will be Out-of-Band data or not. |
SIOCDELRT |
Yes |
Deletes an entry from the interface routing table using the parameters
in the |
SIOCGIFACTIVECONF |
Yes |
Gets a list of interfaces/access points started by P.I.P.S. in an |
SIOCGIFADDR |
Yes |
Gets the interface address. This is valid only for sockets with address family AF_INET. |
SIOCGIFCONF |
No |
Gets a list of available interfaces/access points in an |
SIOCGIFHWADDR |
Yes |
Gets the interface hardware address in an |
SIOCGIFINDEX |
Yes |
Sets the index of an interface/access point from a name in an |
SIOCGIFNUM |
Yes |
Return the total number of IP interfaces configured in the system. |
SIOCIFACTIVESTART |
Yes |
Attempts to start a sub-connection on an available interface/access
point using the parameters in an |
SIOCIFSTART |
Yes |
Attempts to start an interface/access point using the parameters
in an |
SIOCIFSTOP |
Yes |
Stops a previously started interface or sub-connection. |
SIOCSIFNAME |
No |
Sets the desired interface/access point name to start in an |
The follow code for a function shows how to start an interface called "3G Access Point" and return the created socket.
int opensocket() { int Sock; struct ifreq Ifr; char AccessPoint[]="3G Access Point"; //Open the socket Sock = socket(AF_INET, SOCK_STREAM, 0); if (Sock < 0) { printf("\nCannot open socket, error=%d",errno); return -1; } //Set up the interface request structure bzero(&Ifr, sizeof(Ifr)); strcpy(Ifr.ifr_name, AccessPoint); //Set the requested interface name if (ioctl(Sock, SIOCSIFNAME, &Ifr)) { printf("\nCannot set the interface name, error=%d",errno); close(Sock); return -1; } //Start the interface if (ioctl(Sock, SIOCIFSTART, &Ifr)) { printf("\nCannot start the interface, error=%d",errno); close(Sock); return -1; } //Return the opened socket return Sock; }
The Symbian platform does not have any support for long doubles so any P.I.P.S. programs which call long double versions of APIs will actually invoke the double version of the API.
The Symbian platform supports the use of a hardware floating point co-processor, however not all phones incorporate an FPU (Floating Point Unit) and rely on software emulation of floating point operations. Phones and computers equipped with an FPU provide faster and more accurate floating point operations.
The Symbian platform does not support complex numbers so the P.I.P.S. libraries are not able to offer the POSIX complex number APIs.
Note: The mathematical
functions are included in the libm.dll
file.
It is important that the Symbian platform error codes
do not reach any ported application code. P.I.P.S. logically maps the native
OS error codes with the corresponding POSIX errno
values
as per the standard. So, ported programs will not usually have to alter their
error checking/handling.
Mapping P.I.P.S. error codes to the Symbian platform error codes
Porting
your application to the Symbian platform requires 'translating' the Symbian
platform error codes to POSIX error codes. User::Leaves()
from
native Symbian APIs are trapped in P.I.P.S.. Calls to P.I.P.S. APIs from user
code need not be wrapped in TRAP s.
Occasionally
errors may be generated by the underlying Symbian platform that cannot be
translated to POSIX error codes, in which case the error variable errno
will
be out of the usual range of values, above the maximum value of __EMAXERRNO
or 124
.
The Symbian platform error code can be calculated using the following formula:
Symbian Error Code = -(errno - __EMAXERRNO)
Error
codes are defined in the errno.h
file.
P.I.P.S. does not supply a system logger for use with openlog(), syslog() and closelog(). Instead, a rudimentary selection of functions which log to a file can be written as demonstrated by the following example.
//define maximum length of identifier #define SysLogMax 80 //logging file and identifier FILE* fSysLog = NULL; char fSysLogIdent[SysLogMax]; //close the log file void my_closelog() { //close the log file if it is open if (fSysLog) { fclose(fSysLog); fSysLog = NULL; } fSysLogIdent[0] = '\0'; } //open a new log file int my_openlog(const char *ident) { //close the log file if it is open if (fSysLog) my_closelog(); //make the logging directory mkdir("/syslog/", S_IWUSR | S_IRUSR | S_IXUSR); //open a new log file fSysLog = fopen("/syslog/syslog.log", "a"); //return if the log file did not open if (!fSysLog) return -1; //set the identifier if (!ident) fSysLogIdent[0] = '\0'; else strncpy(fSysLogIdent, ident, SysLogMax); return 0; } //output a string to the log file with a variable argument list void my_vsyslog(const char *format, va_list formatlist) { //open a log file if one does not exist if (!fSysLog) { my_openlog(NULL); } //check if there is a log file if (fSysLog) { //print out the logging identifier if one exists if (strlen(fSysLogIdent)) { fprintf(fSysLog, "%s ", fSysLogIdent); } //print out the logging string vfprintf(fSysLog, format, formatlist); fprintf(fSysLog, "\r\n"); } } //output a string to the log file void my_syslog(const char *format, ...) { //create the variable argument list va_list formatlist; va_start(formatlist, format); my_vsyslog(format, formatlist); va_end(formatlist); } int main(int argc, char **argv) { //open the log file my_openlog("test"); //log a line my_syslog("testing logging of the error number : %d", errno); //close the log my_closelog(); return EXIT_SUCCESS; }
At present P.I.P.S. does not provide Pthread barriers, rwlocks or spinlocks, but there are techniques which can be used to implement the functionality using semaphores, mutexes and conditional variables.
The following code sample (originally taken from http://heather.cs.ucdavis.edu/matloff/public_html/158/PLN/ParProcIntro.pdf) demonstrates an implementation of a simple Pthread barrier.
struct barrier_t { //number of nodes to synchronise int nodes; //two counts to avoid race conditions int count[2]; //which count to use int whichcount; //mutex to lock pthread_mutex_t lock; //condition to lock pthread_cond_t cv; }; //initialize a barrier void InitBarrier(struct barrier_t* PB, int nodes) { PB->nodes = nodes; PB->count[0] = 0; PB->count[1] = 0; PB->whichcount = 0; pthread_mutex_init(&PB->lock, NULL); pthread_cond_init(&PB->cv, NULL); } //destroy a barrier void DestroyBarrier(struct barrier_t* PB) { pthread_mutex_destroy(&PB->lock); pthread_cond_destroy(&PB->cv); } //wait for a barrier void WaitBarrier(struct barrier_t* PB) { int WhichCount, Count; //which counter variable to use WhichCount = PB->whichcount; //lock the mutex pthread_mutex_lock(&PB->lock); //get the count of nodes Count = ++PB->count[WhichCount]; //test whether it should block if (Count < PB->nodes) pthread_cond_wait(&PB->cv, &PB->lock); else { //reset the counter PB->count[WhichCount] = 0; PB->whichcount = 1 - WhichCount; //release the wait pthread_cond_broadcast(&PB->cv); } //unlock the threads pthread_mutex_unlock(&PB->lock); }
The following code was posted by Michael M. Lampkin as an open source implementation of Pthread spin threads.
/********************************************************************** BETA User Space spinlocks for POSIX systems lacking this functionality. Copyright ©) 2003-2006 Michael M. Lampkin Contact at michael.lampkin<at>ieee.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License version 2 along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 **********************************************************************/ #define _POSIX_C_SOURCE 200112L #define _XOPEN_SOURCE 600 #define SPINLOCK_SPIN_MAX 50 /** Need this "unique" value that we can use to take any spinlock that has been initialized and identify attempts to call init multiple times without corresponding calls to destroy. A hard coded value should be fine though still a 1 in 4 billion chance of collision with random data in un-inited spinlock. **/ static long int spin_magic = 0x2FCD51F9L; /** The spinlock structure which should NEVER be manipulated by user code. owner: a pthread_t var indicating the current owner of the spinlock or filled with 0's if not owned mutex: the primary mutex that any incoming threads will spin on and attempt to obtain. magic: a field to hold a sentinel value indicating if the spinlock is initialized. **/ typedef struct { pthread_t owner; pthread_mutex_t mutex; long int magic; } spinlock; /** Function: spinlock_init Description: Initializes and allocates system resources to a spinlock structure. Parameters: spin - a pointer to the spinlock structure to be initialized. pshared - either PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED. If the system does not support process shared mutexes or an unknown value is given then defaults internally to a private type with no error. **/ int spinlock_init( spinlock * spin, int pshared ) { int result; pthread_mutexattr_t attr; /* If already inited... race condition with destroy */ if ( NULL == spin ) { return EINVAL; } if ( spin_magic == spin->magic ) { return EBUSY; } ( void ) memset( & spin->owner, 0, sizeof( pthread_t ) ); /* Set our process sharing attribute - default to PRIVATE */ result = pthread_mutexattr_init( & attr ); if ( 0 == result ) { if ( 0 < sysconf( _SC_THREAD_PROCESS_SHARED ) ) { if( PTHREAD_PROCESS_SHARED == pshared ) { result = pthread_mutexattr_setpshared( & attr, pshared ); } else { result = pthread_mutexattr_setpshared( & attr, PTHREAD_PROCESS_PRIVATE ); } } } /* Need to add this to prevent recursive mutex default on some sys */ if ( 0 == result ) { result = pthread_mutexattr_settype( & attr, PTHREAD_MUTEX_ERRORCHECK ); } /* The following is a race against simultaneous calls to init */ if ( 0 == result ) { result = pthread_mutex_init( & spin->mutex, & attr ); } if ( 0 == result ) { spin->magic = spin_magic; } ( void ) pthread_mutexattr_destroy( & attr ); return result; } /** Function: spinlock_destroy Description: Releases system resources allocated to a spinlock structure during initializion. Parameters: spin - a pointer to a previously initialized but not destroyed spinlock. **/ int spinlock_destroy( spinlock * spin ) { int result; if ( NULL == spin || spin_magic != spin->magic ) { return EINVAL; } if ( 0 != ( result = pthread_mutex_destroy( & spin->mutex ) ) ) { return result; } ( void ) memset( & spin->owner, 0, sizeof( pthread_t ) ); /** A return of EINVAL on destroy means another thread is also destroying. Ignore it. **/ spin->magic = 0; return 0; } /** Function: spinlock_lock Description: Attempts to acquire exclusive access to the specified spinlock. If the spinlock is already owned then begin spinning until ownership is obtained. Parameters: spin - a pointer to an initialized spinlock. **/ int spinlock_lock( spinlock * spin ) { pthread_t self; int result; int spin_count; if ( NULL == spin || spin_magic != spin->magic ) { return EINVAL; } self = pthread_self( ); if ( 0 == memcmp( & spin->owner, & self, sizeof( pthread_t ) ) ) { return EDEADLK; } for ( ; 0 != ( result = pthread_mutex_trylock( & spin->mutex ) ) ; ) { if ( EBUSY == result ) { ++ spin_count; if ( SPINLOCK_SPIN_MAX == spin_count ) { ( void ) sched_yield( ); spin_count = 0; } } else { /* Destroy occurred on us... */ return EINVAL; } } ( void ) memcpy( & spin->owner, & self, sizeof( pthread_t ) ); return result; } /**
The Symbian platform phones do not have a command line shell
as standard. P.I.P.S. does however support the stdin(), stdout() and stderr() standard
streams, enabling parent processes to redirect a child's standard streams
for the exchange of data. Without explicitly redirecting the standard streams,
the default is for each to be redirected to the /dev/null
directory.
The P.I.P.S. system() function does not create a shell
for a new program as in most POSIX implementations, however, it will start
a program and return the program's exit value.
Most POSIX based systems interact with a windowing system such as X Windows and libraries such as GTK. P.I.P.S., however, does not have its own UI libraries, so any P.I.P.S. based applications that require UI functionality must rely on the UI from the device manufacturer. As a consequence, for any UI program that uses P.I.P.S., there must be an interaction between the Symbian platform native C++ and P.I.P.S. C code.
Standard C++ states that the One Definition Rule (ODR) must be maintained within a program.
The Symbian platform can neither check (without significant modifications) that the ODR is violated nor use the technique called symbol pre-emption to ensure that the ODR is enforced.
Therefore, you must take care and must not assume that there is only one copy of the symbol (that is, all instances of the same symbol will have the same address) across all DLLs within a program.