ici::doc::pod3::platform(3)
platform ๐ โจ
okay bestie, gather round, because this is the ici::doc::pod3::platform(3) page and it's lowkey a whole vibe โ it's the C software portability definitions and functions situation, from the ICI library functions fam. ๐ซถ
โจ NAME โจ
platform โ C software portability definitions and functions ๐ฆ
๐ธ SYNOPSIS ๐ธ
#include "platform.h"
[see description for available functions]
๐ DESCRIPTION ๐
so platform is literally a library of functions whose entire job is making it easier to port software written in C. it hands you an API that lets your application code reach the resources of an abstract POSIX-compliant "least common denominator" operating system โ which is usually a big old subset of whatever the actual underlying operating system is serving. ๐
most of what the platform library does is aimed at making communication code portable: you get the common functions for shared memory, semaphores, and IP sockets. the implementation of that abstract O/S API shifts around depending on which actual operating system your app is running on, but the API's behavior is always the same. so if your application calls the platform library functions instead of native O/S system calls, yeah, it might give up some O/S-specific capability โ but it gains portability at little if any cost in performance. periodt. ๐
word size differences between platforms? handled by values of the SPACE_ORDER macro. "space order" is the base 2 log of the number of octets in a word: for 32-bit machines the space order is 2 (2^2 = 4 octets per word), and for 64-bit machines it's 3 (2^3 = 8 octets per word). math ate, no cap. ๐งฎโจ
some applications really need a consistent, platform-independent way to represent large integers, and for that purpose platform defines the new types vast and uvast (unsigned vast) which are consistently defined to be 64-bit integers no matter what the platform's native word size is. iconic behavior honestly.
the platform.h header file #includes a whole little squad of the most frequently needed header files: sys/types.h, errno.h, string.h, stdio.h, sys/socket.h, signal.h, dirent.h, netinet/in.h, unistd.h, stdlib.h, sys/time.h, sys/resource.h, malloc.h, sys/param.h, netdb.h, sys/uni.h, and fcntl.h. ๐
beyond all that, platform tries to enhance compatibility by providing standard macros, type definitions, external references, or function implementations that are missing from a few supported O/S's but supported by all the others. and finally, there are entirely new, generic functions provided to establish a common body of functionality that subsumes significantly different O/S-specific capabilities. it's giving "i got you covered." ๐
๐ฉน PLATFORM COMPATIBILITY PATCHES ๐ฉน
the platform library literally "patches" the APIs of supported O/S's to guarantee that all of the following items can be utilized by application software:
- The
strchr(),strrchr(),strcasecmp(), andstrncasecmp()functions. - The
unlink(),getpid(), andgettimeofday()functions. - The
select()function. - The
FD_BITMAPmacro (used byselect()). - The
MAXHOSTNAMELENmacro. - The
NULLmacro. - The
timer_ttype definition.
๐ง PLATFORM GENERIC MACROS AND FUNCTIONS ๐ง
the generic macros and functions in this section may be used in place of comparable O/S-specific functions, to enhance the portability of your code. and bestie, here's the slay: the implementations of these macros and functions are no-ops in environments where they're inapplicable, so they're always safe to call. always. ๐ซถ
FDTABLE_SIZEโ returns the total number of file descriptors defined for the process (or VxWorks target). ๐ION_PATH_DELIMITERโ returns the ASCII character โ either'/'or'\'โ that's used as a directory name delimiter in path names for the file system used by the local platform. ๐ฃ๏ธoK(expression)โ simply casts the value of expression tovoid, which is a way of handling function return codes that are not meaningful in this context. "i see you, i just don't care." ๐CHKERR(condition)โ an "assert" mechanism. it causes the calling function to return-1immediately if condition is false. ๐จCHKZERO(condition)โ an "assert" mechanism. it causes the calling function to return0immediately if condition is false. ๐จCHKNULL(condition)โ an "assert" mechanism. it causes the calling function to returnNULLimmediately if condition is false. ๐จCHKVOID(condition)โ an "assert" mechanism. it causes the calling function to return immediately if condition is false. ๐จvoid snooze(unsigned int seconds)โ suspends execution of the invoking task or process for the indicated number of seconds. nap time fr fr ๐ดvoid microsnooze(unsigned int microseconds)โ suspends execution of the invoking task or process for the indicated number of microseconds. a micro-nap, if you will ๐คvoid getCurrentTime(struct timeval *time)โ returns the current local time in atimevalstructure (seegettimeofday(3C)). โฐvoid isprintf(char *buffer, int bufSize, char *format, ...)โ a safe, portable implementation ofsnprintf(); see thesnprintf(P)man page for details.isprintf()differs fromsnprintf()in that it alwaysNULL-terminates the string in buffer, even if the length of the composed string would equal or exceed bufSize. buffer overruns are reported by log message; and unlikesnprintf(),isprintf()returnsvoid. safety first, babes ๐ก๏ธsize_t istrlen(const char *sourceString, size_t maxlen)โ a safe implementation ofstrlen(); see thestrlen(3)man page for details.istrlen()differs fromstrlen()in that it takes a second argument, the maximum valid length of sourceString. the function returns the number of non-NULLcharacters in sourceString preceding the firstNULLcharacter in sourceString, provided that aNULLcharacter appears somewhere within the first maxlen characters of sourceString; otherwise it returns maxlen. ๐char *istrcpy(char *buffer, char *sourceString, int bufSize)โ a safe implementation ofstrcpy(); see thestrcpy(3)man page for details.istrcpy()differs fromstrcpy()in that it takes a third argument, the total size of the buffer into which sourceString is to be copied.istrcpy()alwaysNULL-terminates the string in buffer, even if the length of the sourceString string would equal or exceed bufSize (in which case sourceString is truncated to fit within the buffer). โ๏ธchar *istrcat(char *buffer, char *sourceString, int bufSize)โ a safe implementation ofstrcat(); see thestrcat(3)man page for details.istrcat()differs fromstrcat()in that it takes a third argument, the total size of the buffer for the string that's being aggregated.istrcat()alwaysNULL-terminates the string in buffer, even if the length of the sourceString string would equal or exceed the sum of bufSize and the length of the string currently occupying the buffer (in which case sourceString is truncated to fit within the buffer). ๐char *igetcwd(char *buf, size_t size)โ normally just a wrapper aroundgetcwd(3). it differs fromgetcwd(3)only whenFSWWDNAMEis defined, in which case the implementation ofigetcwd()must be supplied in an included file named"wdname.c". this adaptation option accommodates flight software environments in which the current working directory name must be configured rather than discovered at run time. very "we don't ask, we're told" energy ๐void isignal(int signbr, void (*handler)(int))โ a portable, simplified interface to signal handling that's functionally indistinguishable fromsignal(P). it assures that reception of the indicated signal will interrupt system calls in SVR4 fashion, even when running on a FreeBSD platform. ๐ฅvoid iblock(int signbr)โ simply prevents reception of the indicated signal by the calling thread. it provides a means of controlling which of the threads in a process will receive the signal cited in an invocation ofisignal(). you get to pick the main character thread ๐char *igets(int fd, char *buffer, int buflen, int *lineLen)โ reads a line of text, delimited by a newline character, from fd into buffer and writes aNULLcharacter at the end of the string. the newline character itself is omitted from theNULL-terminated text line in buffer; and if the newline is immediately preceded by a carriage return character (i.e., the line is from a DOS text file), then the carriage return character is likewise omitted from theNULL-terminated text line in buffer. end of file is interpreted as an implicit newline, terminating the line. if the number of characters preceding the newline is greater than or equal to buflen, only the first (buflen - 1) characters of the line are written into buffer.- on error the function sets
*lineLento-1and returnsNULL. ๐ญ - on reading end-of-file, the function sets
*lineLento zero and returnsNULL. - otherwise the function sets
*lineLento the length of the text line in buffer, as if fromstrlen(3), and returns buffer. ๐
- on error the function sets
int iputs(int fd, char *string)โ writes to fd theNULL-terminated character string at string. no terminating newline character is appended to string byiputs(). on error the function returns-1; otherwise the function returns the length of the character string written to fd, as if fromstrlen(3). โ๏ธvast strtovast(char *string)โ converts the leading characters of string, skipping leading white space and ending at the first subsequent character that can't be interpreted as contributing to a numeric value, to avastinteger and returns that integer. ๐ขuvast strtouvast(char *string)โ same asstrtovast()except the result is an unsignedvastinteger value. ๐ขvoid findToken(char **cursorPtr, char **token)โ locates the next non-whitespace lexical token in a character array, starting at*cursorPtr. the functionNULL-terminates that token within the array and places a pointer to the token in*token. it also accommodates tokens enclosed within matching single quotes, which may contain embedded spaces and escaped single-quote characters. if no token is found,*tokencontainsNULLon return from this function. ๐ต๏ธโ๏ธvoid *acquireSystemMemory(size_t size)โ usesmemalign()to allocate a block of system memory of length size, starting at an address that's guaranteed to be an integral multiple of the size of a pointer tovoid, and initializes the entire block to binary zeroes. returns the starting address of the allocated block on success; returnsNULLon any error. ๐ง โจint createFile(const char *name, int flags)โ creates a file of the indicated name, using the indicated file creation flags. this function provides common file creation functionality across VxWorks and Unix platforms, invokingcreat()under VxWorks andopen()elsewhere. for return values, seecreat(2)andopen(2). ๐unsigned int getInternetAddress(char *hostName)โ hands you back the IP address of the host machine you named, or zero if it just can't figure it out ๐char *getInternetHostName(unsigned int hostNbr, char *buffer)โ drops the host name of the machine athostNbrintobufferand returnsbuffer, or returnsNULLif literally anything goes wrong. Size yourbufferat(MAXHOSTNAMELEN + 1)bestie, don't skimp ๐int getNameOfHost(char *buffer, int bufferLength)โ writes the first(bufferLength - 1)characters of the local machine's host name intobuffer. Returns0when it slays,-1on any error.unsigned int getAddressOfHost()โ gives you the IP address for the local machine's host name, or0if anything's off.void parseSocketSpec(char *socketSpec, unsigned short *portNbr, unsigned int *hostNbr)โ takessocketSpecapart and pulls the host number (IP address) and port number out of the string. The expected format is"{ @ | hostname }[:<portnbr>]", where@means "the host name of the local machine" ๐ . If it can suss out the host number, it writes it into*hostNbr; if not, it writes0into*hostNbr. If a port number is given and lands in the range1024to65535, it goes into*portNbr; otherwise0goes into*portNbr.void printDottedString(unsigned int hostNbr, char *buffer)โ builds that classic dotted-string (xxx.xxx.xxx.xxx) glow-up of the IPv4 address sitting inhostNbrand writes it intobuffer.bufferhas to be at least 16 long, no negotiating โจchar *getNameOfUser(char *buffer)โ writes the user name of whoever's invoking the task or process intobufferand returnsbuffer.buffermust be at leastL_cuseridbig, which is a constant chillin' in thestdio.hheader file. Returnsbuffer, obviously ๐ซถint reUseAddress(int fd)โ makes the address bound to the socketfdreusable, so you can close that socket and immediately reopen and re-bind it to the same port number. Returns0on success,-1on any error.int makeIoNonBlocking(int fd)โ makes I/O on the socketfdnon-blocking; returns-1on failure. Here's the tea: if you try to read on a non-blocking socket when nothing's pending, or write when its output buffer is full, it will not block. Instead it returns-1and setserrnotoEWOULDBLOCK. Iconic behaviour honestly.int watchSocket(int fd)โ flips on the"linger"and"keepalive"options for the socketfd. Peepsocket(2)for the details. Returns0on success,-1on any failure.void closeOnExec(int fd)โ makes surefdis NOT open in any child processfork()ed from the invoking process. Heads up though: this does absolutely nothing on a VxWorks platform ๐
๐ฉน EXCEPTION REPORTING ๐ฉน
The functions in this section give you platform-independent ways to report on processing exceptions. Very demure, very portable ๐
The whole mechanism underneath ICI's exception reporting is a pair of functions that stash error messages in a privately managed pool of static memory. Those two โ postErrmsg() and postSysErrmsg() โ are built to return super fast with zero chance of failing themselves. That said, they are not safe to call from an interrupt service routine (ISR). Even though each one just copies its text into the next free spot in the error message memory pool, that pool is protected by a mutex โ and multiple processes might be queued up waiting on that mutex, so the total time to run the function is non-deterministic. No cap.
Built on top of postErrmsg() and postSysErrmsg() are putErrmsg() and putSysErrmsg(), which can take longer to return. Each one just calls the matching "post" function, but then calls writeErrmsgMemos(), which calls writeMemo() to print (or otherwise deliver) every message currently posted to the pool, then destroys all those posted messages and empties the pool. Clean sweep ๐งน
The recommended general policy on using ICI's exception reporting functions โ which the functions in the ION distribution libraries are supposed to follow โ goes like this:
In the implementation of any ION library function or any ION task's top-level driver function, any condition that prevents the function from continuing execution toward producing the effect it is designed to produce is considered an "error".
Detection of an error should result in the printing of an error message and, normally, the immediate return of whatever return value is used to indicate the failure of the function in which the error was detected. By convention this value is usually-1, but both zero andNULLare appropriate failure indications under some circumstances such as object creation.
TheCHKERR,CHKZERO,CHKNULL, andCHKVOIDmacros are used to implement this behavior in a standard and lexically terse manner. Use of these macros offers an additional feature: for debugging purposes, they can easily be configured to callsm_Abort()to terminate immediately with a core dump instead of returning a error indication. This option is enabled by setting the compiler parameterCORE_FILE_NEEDEDto1at compilation time.
In the absence of either any error, the function returns a value that indicates nominal completion. By convention this value is usually zero, but under some circumstances other values (such as pointers or addresses) are appropriate indications of nominal completion. Any additional information produced by the function, such as an indication of "success", is usually returned as the value of a reference argument. [Note, though, that database management functions and the SDR hash table management functions deviate from this rule: most return0to indicate nominal completion but functional failure (e.g., duplicate key or object not found) and return1to indicate functional success.]
So when returning a value that indicates nominal completion of the function โ even if the result might be interpreted as a failure at a higher level (e.g., an object identified by a given string is not found, through no failure of the search function) โ do NOT invoke putErrmsg().
UseputErrmsg()andputSysErrmsg()only when functions are unable to proceed to nominal completion. UsewriteMemo()orwriteMemoNote()if you just want to log a message.
Whenever returning a value that indicates an error:
- If the failure is due to the failure of a system call or some other non-ION function, assume thaterrnohas already been set by the function at the lowest layer of the call stack; useputSysErrmsg(orpostSysErrmsgif in a hurry) to describe the nature of the activity that failed. The text of the error message should normally start with a capital letter and should NOT end with a period. - Otherwise โ i.e., the failure is due to a condition that was detected within ION โ useputErrmsg(orpostErrmgif pressed for time) to describe the nature of the failure condition. This will aid in tracing the failure through the function stack in which the failure was detected. The text of the error message should normally start with a capital letter and should end with a period.
When a failure in a called function is reported to "driver" code in an application program, before continuing or exiting use writeErrmsgMemos() to empty the message pool and print a simple stack trace identifying the failure.
char *system_error_msg( )โ returns a short lil text string describing the current system error, as identified by the current value oferrno.void setLogger(Logger usersLoggerName)โ sets the user function that writes messages to a user-defined "log" medium. The logger function's calling sequence has to match this prototype exactly:
void usersLoggerName(char *msg);
The default Logger function just writes the message to standard output. Simple. Iconic. ๐ธ
void writeMemo(char *msg)โ writes one log message, using the message logging function that's currently defined.void writeMemoNote(char *msg, char *note)โ writes a log message just likewriteMemo(), but with the user-supplied, context-specific text innotetagging along ๐ซถvoid writeErrMemo(char *msg)โ writes a log message likewriteMemo(), plus text describing the current system error.char *itoa(int value)โ returns a string representation of the signed integer invalue, nominally so you can immediately use it as an argument toputErrmsg(). [Big note: the string gets built in a static buffer, so this function is not thread-safe. ๐]char *utoa(unsigned int value)โ returns a string representation of the unsigned integer invalue, nominally for immediate use as an argument toputErrmsg(). [Same warning bestie: the string is constructed in a static buffer; this function is not thread-safe. ๐]void postErrmsg(char *text, char *argument)โ constructs an error message that notes the name of the source file containing the line where this function was called, the line number, the text of the message, and โ if it's notNULLโ a single textual argument you can use to give more specific info about the nature of the reported failure (like the value of one of the arguments to the failed function). The error message gets appended to the list of messages in a privately managed pool of static memory that'sERRMSGS_BUFSIZEbytes long.
If text is NULL, is a string of zero length, or starts with a newline character (i.e., *text == '\0' or '\n'), the function returns immediately and no error message is recorded.
The errmsgs pool is designed to be big enough to hold error messages from every level of the calling stack at the moment an error is hit. But if the unused space left in the pool is smaller than the size of the new error message, that message gets silently omitted. In that case, as long as at least two bytes of unused space remain in the pool, a message made of a single newline character gets appended to the list to signal that a message was dropped for being too long. Not the vibe, but that's the deal ๐
void postSysErrmsg(char *text, char *arg)โ bestie this ispostErrmsg()but โจextraโจ: the message it builds also tacks on text describing whatever the current system error is. Andtextgets truncated as needed so that its length plus the length of that system-error description never goes over 1021 bytes. No cap. ๐ซถint getErrmsg(char *buffer)โ pulls the oldest error message out of the message pool, drops it intobuffer, and yeets it from the pool so new messages have room to breathe. Returns zero if the pool can't be locked for update OR if there are no more messages left in the pool; otherwise it returns the length of the message it copied intobuffer. Heads up for safety: the size ofbuffershould beERRMSGS_BUFSIZE. ๐
And lowkey important: if a returned error message is just a single newline character, that always means an error message got silently omitted because there wasn't enough space left on the message pool to hold it. ๐
void writeErrmsgMemos( )โ callsgetErrmsg()over and over until the pool is empty, usingwriteMemo()to log every single message in the pool. Messages that got omitted for being too long show up as logged lines that look like"[message omitted due to excessive length]".void putErrmsg(char *text, char *argument)โ literally just callspostErrmsg()and thenwriteErrmsgMemos(). That's the whole gig. ๐ธvoid putSysErrmsg(char *text, char *arg)โ and this one literally just callspostSysErrmsg()and thenwriteErrmsgMemos(). Matching set, iconic. โจvoid discardErrmsgs( )โ callsgetErrmsg()repeatedly until the pool is empty, throwing away every message. Gone. Not sorry. ๐void printStackTrace( )โ Linux machines only, bb! UseswriteMemo()to print a trace of the process's current execution stack, starting at the lowest level</> of the stack and going all the way up to themain()function of the executable.
Real talk, three things to know: (a) printStackTrace() is only implemented for Linux platforms at this time; (b) symbolic names of functions can only be printed if the -rdynamic flag was enabled when the executable was linked; (c) only the names of non-static functions will show up in the stack trace. ๐ฆ
Want the full picture of what the executable was doing when that stack trace snapshot got taken? Use the Linux addr2line tool. To do it, cd into a directory where the executable file lives (like /opt/bin) and fire off an addr2line command like this:
addr2line -e name_of_executable stack_frame_address
where both name_of_executable and stack_frame_address are taken from one of the lines of the printed stack trace. addr2line will print the source file name and line number for that stack frame. Obsessed. ๐
๐ WATCH CHARACTERS ๐
The functions in this section serve up platform-independent capabilities for recording "watch" characters that flag when protocol events happen. Peep bprc(5), ltprc(5), cfdprc(5), etc. for the deets on the watch character production options that the protocol packages give you. ๐
void setWatcher(Watcher usersWatcherName)โ sets the user function that gets used for recording watch characters to some user-defined "watch" medium. The watcher function's calling sequence has to match this prototype exactly:
void usersWatcherName(char token);
The default Watcher function is simple: it just writes the token to standard output. periodt. ๐
void iwatch(char token)โ records one "watch" character, using whatever watch character recording function is currently defined.
โญ SELF-DELIMITING NUMERIC VALUES (SDNV) โญ
The functions in this section encode and decode SDNVs โ portable, variable-length numeric variables that expand to whatever size is needed to hold the values inside them. SDNVs get used extensively in the BP and LTP libraries. ๐ง
void encodeSdnv(Sdnv *sdnvBuffer, uvast value)โ figures out how many octets of SDNV text are needed to containvalue, sticks that number into the length field of the SDNV buffer, and encodesvaluein SDNV format into the first length octets of the text field of the SDNV buffer. ๐int decodeSdnv(uvast *value, unsigned char *sdnvText)โ determines the length of the SDNV sitting atsdnvTextand returns that number, after extracting the SDNV's value from those octets and storing it invalue. Returns0if the encoded number value won't fit into an unsigned vast integer. ๐ญ
๐ฅ ARITHMETIC ON LARGE INTEGERS (SCALARS) ๐ฅ
The functions in this section do simple arithmetic on unsigned Scalar objects โ structures that wrap up big positive integers in a machine-independent way. Every Scalar is made of two integers: a count of units [ranging from 0 to (2^30 - 1), i.e., up to 1 gig] and a count of gigs [ranging from 0 to (2^31 -1)]. So a Scalar can represent a numeric value up to 2 billion billions, i.e., 2 million trillions. main character numbers only. ๐
void loadScalar(Scalar *scalar, signed int value)โ sets the value ofscalarto the absolute value ofvalue.void increaseScalar(Scalar *scalar, signed int value)โ adds toscalarthe absolute value ofvalue.void reduceScalar(Scalar *scalar, signed int value)โ adds toscalarthe absolute value ofvalue. (yeah bestie, that's what it says ๐ญ)void multiplyScalar(Scalar *scalar, signed int value)โ multipliesscalarby the absolute value ofvalue.void divideScalar(Scalar *scalar, signed int value)โ dividesscalarby the absolute value ofvalue.void copyScalar(Scalar *to, Scalar *from)โ copies the value offromintoto.void addToScalar(Scalar *scalar, Scalar *increment)โ addsincrement(aScalar, not a C integer) toscalar.void subtractFromScalar(Scalar *scalar, Scalar *decrement)โ subtractsdecrement(aScalar, not a C integer) fromscalar.int scalarIsValid(Scalar *scalar)โ returns1if the arithmetic done onscalarhasn't caused overflow or underflow.int scalarToSdnv(Sdnv *sdnv, Scalar *scalar)โ ifscalarpoints to a validScalar, stores the value ofscalarinsdnv; otherwise sets the length ofsdnvto zero.int sdnvToScalar(Scalar *scalar, unsigned char *sdnvText)โ ifsdnvTextpoints to a sequence of bytes that, when read as the text of an Sdnv, has a value representable in a 61-bit unsigned binary integer, then this function stores that value inscalarand returns the detected Sdnv length. Otherwise it returns zero.
Quick note: Scalars and Sdnvs are both representations of potentially large unsigned integer values, and any Scalar can alternatively be represented as an Sdnv. BUT โ it is possible for a valid Sdnv to be too large to represent in a Scalar. ๐
๐ PRIVATE MUTEXES ๐
The functions in this section give you platform-independent management of mutexes for synchronizing the operations of threads or tasks living in a common private address space. ๐ซถ
int initResourceLock(ResourceLock *lock)โ establishes an inter-thread lock for use in locking some resource. Returns0if it worked,-1if it did not. ๐ญvoid killResourceLock(ResourceLock *lock)โ deletes the resource lock thatlockrefers to. bye bb ๐void lockResource(ResourceLock *lock)โ checks the state oflock. If the lock is already owned by a different thread, the call blocks until the other thread gives it up. If the lock is unowned, it gets handed to the current thread and the lock count is set to1. If the lock is already owned by this thread, the lock count gets incremented by1.void unlockResource(ResourceLock *lock)โ if it's called by the current owner oflock, decrementslock's lock count by1; if that hits zero, it relinquishes the lock so other threads can take it.
Care must be taken to make sure that one, and only one,unlockResource()call is issued for eachlockResource()call issued on a given resource lock. Not a vibe you want to fumble, fr fr. ๐
๐ SHARED MEMORY IPC DEVICES ๐
The functions in this section give you platform-independent management of IPC mechanisms for synchronizing the operations of threads, tasks, or processes that may live in different address spaces but share access to a common system (nominally, processor) memory. ๐ฆ
NOTE that this is distinct from the VxWorks "VxMP" capability that lets tasks share access to bus memory or dual-ported board memory from multiple processors. The "platform" system will support IPC devices that use that capability at some time in the future, but that support is not yet implemented. ๐
int sm_ipc_init( )โ acquires and initializes shared-memory IPC management resources. Must be called before any other shared-memory IPC function is called. Returns0on success,-1on any failure. โจvoid sm_ipc_stop( )โ releases shared-memory IPC management resources, disabling the shared-memory IPC functions untilsm_ipc_init()gets called again.int sm_GetUniqueKey( )โ some of thesm_(shared memory) functions described below associate new communication objects with key values that uniquely identify them, so different processes can access them independently. Key values are typically defined as constants in application code. But when you need a new communication object and nobody anticipated it in the application, you can callsm_GetUniqueKey()to grab a brand-new, arbitrary key value that's known not to already be in use. ๐sm_SemId sm_SemCreate(int key, int semType)โ creates a shared-memory semaphore you can use to synchronize activity among tasks or processes that live in a common system memory but possibly in multiple address spaces; returns a reference handle for that semaphore, orSM_SEM_NONEon any failure. Ifkeyrefers to an existing semaphore, it returns the handle of that semaphore. Ifkeyis the constant valueSM_NO_KEY, it automatically obtains an unused key. On VxWorks platforms,semTypedecides the order in which the semaphore gets given to multiple tasks that try to take it while it's already taken: if set toSM_SEM_PRIORITYthen the semaphore goes to tasks in task priority sequence (i.e., the highest-priority task waiting for it receives it when it's released), while otherwise (SM_SEM_FIFO) the semaphore goes to tasks in the order they attempted to take it. On all other platforms, onlySM_SEM_FIFObehavior is supported andsemTypeis ignored. ๐int sm_SemTake(sm_SemId semId)โ blocks until the indicated semaphore is no longer taken by any other task or process, then takes it. Returns0on success,-1on any error. ๐
void sm_SemGive(sm_SemId semId)
Gives the semaphore you point at, so some other task or process can come along and take it. That's it, that's the whole job โ it's the "you may go now bestie" of semaphore calls. ๐
void sm_SemEnd(sm_SemId semId)
This is how you send a termination signal to whatever task is currently blocked on taking that semaphore (if literally anyone is). It flips the "ended" flag on that semaphore to 1 โ so a call to sm_SemEnded() will come back with 1 โ and then it gives the semaphore, so the blocked task actually gets a turn and can go test that flag. Like sliding into their DMs with the bad news. ๐ญ
int sm_SemEnded(sm_SemId semId)
Returns 1 if the "ended" flag on the indicated semaphore has been set to 1, and returns zero if it hasn't. And here's the cute part: when it returns 1, it also gives the semaphore, so any other tasks that might be pended on that same semaphore get a shot at testing it and finding out it's been ended too. Group chat announcement energy. ๐ฃ
void sm_SemUnend(sm_SemId semId)
Resets an ended semaphore, so a restarted subsystem can just reuse it instead of deleting it and allocating a whole new one. Recycling is iconic, actually. โป๏ธโจ
int sm_SemUnwedge(sm_SemId semId, int timeoutSeconds)
The rescue mission function ๐ โ for releasing semaphores that got taken and then never released, probably because the tasks or processes holding them crashed before they could let go. Rude of them.
It attempts to take the semaphore. If that attempt doesn't succeed within timeoutSeconds seconds โ and that window is there to give normal processing time to finish, in case the semaphore is legitimately and temporarily locked by some task โ then the semaphore is assumed to be wedged. Either way, once all that's decided, the semaphore gets released.
Returns 0 on success, -1 on any error.
void sm_SemDelete(sm_SemId semId)
Destroys the indicated semaphore. Straight up. ๐
sm_SemId sm_GetTaskSemaphore(int taskId)
Returns the ID of the semaphore that's dedicated to the private use of the task you named, or SM_SEM_NONE on any error.
So here's the whole concept behind it: for every task there can always be one dedicated semaphore, one it can always use for its own purposes, and whose key value you can know ahead of time โ because the key of that semaphore is based on the task's ID. ๐
The design rests on one assumption: that each task's ID โ whether it's a VxWorks task ID or a Unix process ID โ maps to a number that's out of the range of all possible key values that sm_GetUniqueKey() might arbitrarily produce.
- For VxWorks, we assume this is true because a task ID is a pointer to task state in memory, and we assume that doesn't exceed 2GB. The unique key counter starts at 2GB. ๐ง
- For Unix, we assume it's true because a process ID is an index into a process table whose size is less than 64K. Unique keys are formed by shifting the process ID left 16 bits and adding the value of an incremented counter, which is always greater than zero.
int sm_ShmAttach(int key, int size, char **shmPtr, int *id)
Attaches to a segment of memory that tasks or processes living in a common system memory โ but possibly in multiple address spaces โ all have access to. Shared memory besties ๐ซถ.
This function registers the invoking task or process as a user of the shared memory segment identified by key.
- If
keyis the constant valueSM_NO_KEY, it automatically setskeyto some unused key value. - If a shared memory segment identified by
keyalready exists, thensizemay be zero and the value of*shmPtris ignored. - Otherwise the size of the shared memory segment must be provided in
size, and a new shared memory segment is created in a manner that depends on*shmPtr:- if
*shmPtrisNULL, thensizebytes of shared memory are dynamically acquired, allocated, and assigned to the newly created shared memory segment; - otherwise the memory located at
shmPtris assumed to have been pre-allocated and is merely assigned to the newly created shared memory segment.
- if
On success it stores the unique shared memory ID of the segment in *id (for possible future destruction, iykyk), stores a pointer to the segment's assigned memory in *shmPtr, and returns 1 if the segment is newly created or 0 otherwise. Returns -1 on any error. ๐ฅ
void sm_ShmDetach(char *shmPtr)
Unregisters the invoking task or process as a user of the shared memory starting at shmPtr. Not me leaving the group chat ๐ช๐จ
void sm_ShmDestroy(int id)
Destroys the shared memory segment identified by id, releasing any memory that was allocated when the segment was created. Gone, reduced to atoms, periodt. ๐โจ
๐ฆ PORTABLE MULTI-TASKING ๐ฆ
int sm_TaskIdSelf( )
Returns the unique identifying number of the invoking task or process. It's giving "who am I, really" but make it a syscall. ๐ช
int sm_TaskExists(int taskId)
Returns non-zero if a task or process identified by taskId is currently running on the local processor, and zero otherwise. A little vibe check for your task list. โ
void *sm_TaskVar(void **arg)
Posts or retrieves the value of the "task variable" belonging to the invoking task.
Every task gets access to a single task variable, initialized to NULL, that lives in the task's private state. This is genuinely convenient for passing task-specific info to a signal handler, for example โ lowkey one of the handiest little tricks in here.
If arg is non-NULL, then *arg is posted as the new value of the task's private task variable. And in any case, the value of that task variable is returned. ๐
void sm_TaskSuspend( )
Indefinitely suspends execution of the invoking task or process. Helpful when you want to freeze an application right at the point where an error is detected, then go in with a debugger and examine its state. It's the "hold still, I'm looking at you" of debugging. ๐
void sm_TaskDelay(int seconds)
Same as snooze(3). ๐ด
void sm_TaskYield( )
Relinquishes the CPU temporarily so other tasks can use it. Sharing is caring, bestie. ๐
sm_TaskSpawn
int sm_TaskSpawn(char *name, char *arg1, char *arg2, char *arg3, char *arg4, char *arg5, char *arg6, char *arg7, char *arg8, char *arg9, char *arg10, int priority, int stackSize)
Spawns/forks a new task/process and hands it up to ten command-line arguments. name is the name of the function (VxWorks) or the executable image (UNIX) to be executed in the new task/process. โจ
- For UNIX,
namemust be the name of some executable program in the$PATHof the invoking process. - For VxWorks,
namemust be the name of some function named in an application-defined private symbol table (ifPRIVATE_SYMTABis defined) or the system symbol table (otherwise). IfPRIVATE_SYMTABis defined, the application must provide a suitable adaptation of thesymtab.csource file, which implements the private symbol table. That part is on you, not on us. ๐
"priority" and "stackSize" are ignored under UNIX. Under VxWorks, if they're zero they default to the values in the application-defined private symbol table if one is provided, or otherwise to ICI_PRIORITY (nominally 100) and 32768 respectively.
Returns the task/process ID of the new task/process on success, or -1 on any error.
void sm_TaskKill(int taskId, int sigNbr)
Sends the indicated signal to the indicated task or process. ๐จ
void sm_TaskDelete(int taskId)
Terminates the indicated task or process. Blocked. ๐ซ
void sm_Abort()
Terminates the calling task or process. If it's not called while ION is in flight configuration, a stack trace is printed or a core file is written.
๐ Heads up bestie: this one takes you out, and it might leave a stack trace or a core file behind as evidence.
int pseudoshell(char *script)
Parses script into a command name and up to 10 arguments, then passes the command name and arguments to sm_TaskSpawn() for execution. The sm_TaskSpawn() function is invoked with priority and stack size both set to zero, which causes default values (possibly from an application-defined private symbol table) to be used.
Tokens in script are normally whitespace-delimited, but a token enclosed in single-quote characters (') may contain embedded whitespace and may contain escaped single-quote characters ("\'").
On any parsing failure it returns -1; otherwise it returns the value returned by sm_TaskSpawn(). ๐
๐ USER'S GUIDE ๐
Compiling an application that uses "platform":
Just be sure to #include "platform.h" at the top of each source file that includes any platform function calls. That's literally it. ๐ธ
#include "platform.h"
Linking/loading an application that uses "platform":
- In a Solaris environment, link with these libraries:
-lplatform -socket -nsl -posix4 -c
- In a Linux environment, simply link with platform:
-lplatform
- In a VxWorks environment, use
ld 1, 0, "libplatform.o"
to load platform on the target before loading applications. Load the library before the apps, not after โ order matters, no cap. ๐
โญ SEE ALSO โญ
gettimeofday(3C)
And that's the whole page, bestie โ you made it to the end. ๐ This one came from the perl v5.24.1 toolchain, dated 2016-07-07, and it's page ici::doc::pod3::platform(3). Now go forth and slay your semaphores. ๐
๐ฆ
๐ฆ where this page comes from
- ubuntu jammy: ion-doc 3.2.1+dfsg-1.1 ยท BSD-3-Clause
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms