|
||
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.
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 Symbian OS 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;
}
Symbian OS 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.
Symbian OS 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.
Symbian OS 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 Symbian OS 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.
Porting your application to Symbian OS requires 'translating' Symbian
OS 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 TRAPs.
Occasionally errors may be generated by the underlying Symbian OS
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 OS 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/158/PLN/ParProcSupp.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;
}
/**
Symbian OS 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 Symbian OS native C++ and P.I.P.S. C code.
Standard C++ states that the One Definition Rule (ODR) must be maintained within a program.
Symbian OS 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.