ici::doc::pod3::sdr(3)
π sdr β Simple Data Recorder library π
okay so first things first: this page lives in section 3 of the manual, under ici::doc::pod3::sdr(3) β ICI library functions, iykyk. And bestie, it's a long one, so buckle up. πΈ
β¨ SYNOPSIS β¨
#include "sdr.h"
[see below for available functions]
that's it, that's the whole synopsis. the rest is vibes and functions, and the functions are listed below, so keep scrolling. π
π¦ DESCRIPTION π¦
SDR is a library of functions that support the use of an abstract data recording device called an "SDR" ("simple data recorder") for persistent storage of data. The SDR abstraction insulates software not only from the specific characteristics of any single data storage device but also from some kinds of persistent data storage and retrieval chores. The underlying principle? An SDR provides standardized support for user data organization at object granularity, with direct access to persistent user data objects, rather than supporting user data organization only at "file" granularity and requiring the user to implement access to the data objects accreted within those files. no cap. π«Ά
The SDR library is designed to provide some of the same kinds of directory services as a file system, plus support for complex data structures that give you way more operational flexibility than files. (As an example of this flexibility, consider how much easier and faster it is to delete a given element from the middle of a linked list than it is to delete a range of bytes from the middle of a text file. like, it's not even close. π) The intent is to enable the software developer to take maximum advantage of the high speed and direct byte addressability of a non-volatile flat address space in the management of persistent data. The SDR equivalent of a "record" of data is simply a block of nominally persistent memory allocated from this address space. The SDR equivalent of a "file" is a collection object. Like files, collections can have names, can be located by name within persistent storage, and can impose structure on the data items they encompass. But β as discussed later β SDR collection objects can impose structures other than the strict FIFO accretion of records or bytes that characterizes a file. iconic behavior honestly. β¨
The notional data recorder managed by the SDR library takes the form of a single array of randomly accessible, contiguous, nominally persistent memory locations called a heap. Physically, the heap may be implemented as:
- a region of shared memory,
- a single file of predefined size, or
- both β that is, the heap may be a region of shared memory that is automatically mirrored in a file.
SDR services that manage SDR data are provided in several layers, each of which relies on the services implemented at lower levels:
- At the highest level, a cataloguing service enables retrieval of persistent objects by name.
- Services that manage three types of persistent data collections are provided for use both by applications and by the cataloguing service: linked lists, self-delimiting tables (which function as arrays that remember their own dimensions), and self-delimiting strings (short character arrays that remember their lengths, for speedier retrieval).
- Basic SDR heap space management services, analogous to
malloc()andfree(), enable the creation and destruction of objects of arbitrary type. - Farther down the service stack are memcpy-like low-level functions for reading from and writing to the heap.
- Protection of SDR data integrity across a series of reads and writes is provided by a transaction mechanism.
SDR persistent data are referenced in application code by Object values and Address values, both of which are simply displacements (offsets) within SDR address space. The difference between the two is that an Object is always the address of a block of heap space returned by some call to sdr_malloc(), while an Address can refer to any byte in the address space. That is, an Address is the SDR functional equivalent of a C pointer in DRAM, and some Addresses point to Objects. π«
Before using SDR services, the services must be loaded to the target machine and initialized by invoking the sdr_initialize() function, and the management profiles of one or more SDR's must be loaded by invoking the sdr_load_profile() function. These steps are normally performed only once, at application load time.
An application gains access to an SDR by passing the name of the SDR to the sdr_start_using() function, which returns an Sdr pointer. Most other SDR library functions take an Sdr pointer as first argument.
β οΈ All writing to an SDR heap must occur during a transaction that was initiated by the task issuing the write. no sneaky side-writes, bestie.
Transactions are single-threaded; if task B wants to start a transaction while a transaction begun by task A is still in progress, it must wait until A's transaction is either ended or cancelled. A transaction is begun by calling sdr_begin_xn(). The current transaction is normally ended by calling the sdr_end_xn() function, which returns an error return code value in the event that any serious SDR-related processing error was encountered in the course of the transaction. Transactions may safely be nested, provided that every level of transaction activity that is begun is properly ended. periodt. π
The current transaction may instead be cancelled by calling sdr_cancel_xn(), which is normally used to indicate that some sort of serious SDR-related processing error has been encountered. Canceling a transaction reverses all SDR update activity performed up to that point within the scope of the transaction β and, if the canceled transaction is an inner, nested transaction, all SDR update activity performed within the scope of every outer transaction encompassing that transaction and every other transaction nested within any of those outer transactions β provided the SDR was configured for transaction reversibility. When an SDR is configured for reversibility, all heap write operations performed during a transaction are recorded in a log file that is retained until the end of the transaction. Each log file entry notes the location at which the write operation was performed, the length of data written, and the content of the overwritten heap bytes prior to the write operation. Canceling the transaction causes the log entries to be read and processed in reverse order, restoring all overwritten data. Ending the transaction, on the other hand, simply causes the log to be discarded. π
If a log file exists at the time that the profile for an SDR is loaded (typically during application initialization), the transaction that was being logged is automatically canceled and reversed. This ensures that, for example, a power failure that occurs in the middle of a transaction will never wreck the SDR's data integrity: either all updates issued during a given transaction are reflected in the current database content or none are. it's giving "all or nothing" and honestly we're obsessed. π«Ά
As a further measure to protect SDR data integrity, an SDR may additionally be configured for object bounding. When an SDR is configured to be "bounded", every heap write operation is restricted to the extent of a single object allocated from heap space; that is, it's impossible to overwrite part of one object by writing beyond the end of another. To enable the library to enforce this mechanism, application code is prohibited from writing anywhere but within the extent of an object that either:
- (a) was allocated from managed heap space during the same transaction (directly or indirectly via some collection management function), or
- (b) was staged β identified as an update target β during the same transaction (again, either directly or via some collection management function).
Note that both transaction reversibility and object bounding consume processing cycles and inhibit performance to some degree. Determining the right balance between operational safety and processing speed is left to the user. your call bestie, i don't make the rules π
Note also that, since SDR transactions are single-threaded, they can additionally be used as a general mechanism for simply implementing "critical sections" in software that is already using SDR for other purposes: the beginning of a transaction marks the start of code that can't be executed concurrently by multiple tasks. To support this use of the SDR transaction mechanism, the additional transaction termination function sdr_exit_xn() is provided. sdr_exit_xn() simply ends a transaction without either signaling an error or checking for errors. Like sdr_cancel_xn(), sdr_exit_xn() has no return value; unlike sdr_cancel_xn(), it assures that ending an inner, nested transaction does not cause the outer transaction to be aborted and backed out.
π But this capability must be used carefully: the protection of SDR data integrity requires that transactions which are ended by sdr_exit_xn() must not encompass any SDR update activity whatsoever. not a single byte. fr fr.
The heap space management functions of the SDR library are adapted directly from the Personal Space Management (psm) function library. The manual page for psm(3) explains the algorithms used and the rationale behind them. The principal difference between PSM memory management and SDR heap management is that, for performance reasons, SDR reserves the "small pool" for its own use only; all user data space is allocated from the "large pool", via the sdr_malloc() function.
π RETURN VALUES AND ERROR HANDLING π
Whenever an SDR function call fails, a diagnostic message explaining the failure of the function is recorded in the error message pool managed by the "platform" system (see the discussion of putErrmsg() in platform(3)). π«Ά
The failure of any function invoked in the course of an SDR transaction causes all subsequent SDR activity in that transaction to fail immediately. This can streamline SDR application code somewhat: it may not be necessary to check the return value of every SDR function call executed during a transaction. If the sdr_end_xn() call returns zero, all updates performed during the transaction must have succeeded. slay. β¨
β¨ SYSTEM ADMINISTRATION FUNCTIONS β¨
int sdr_initialize(int wmSize, char *wmPtr, int wmKey, char *wmName)
okay bestie, listen up. This is the origin story of your SDR system. You must call sdr_initialize() once every time the computer reboots, before you call any other SDR library function. No exceptions. π
It attaches to a pool of shared memory managed by PSM (peep psm(3) for the tea) that makes SDR library operations possible. Now, if your SDR system needs to share a common pool of shared memory with one or more other systems, you gotta put the key of that shared memory segment in wmKey and the PSM partition name associated with that memory segment in wmName. Otherwise, wmKey must be zero and wmName must be NULL, which makes sdr_initialize() assign default values. Easy.
If a shared memory segment identified by the effective value of wmKey already exists, then wmSize can be zero and wmPtr is ignored. Otherwise, you must provide the size of the shared memory pool in wmSize, and a new shared memory segment gets created based on wmPtr:
- if
wmPtrisNULL, thenwmSizebytes of shared memory are dynamically acquired, allocated, and assigned to the newly created shared memory segment; - otherwise, the memory located at
wmPtris assumed to have been pre-allocated and is merely assigned to the newly created shared memory segment.
sdr_initialize() also creates a semaphore to serialize access to the SDR system's private array of SDR profiles. Iconic.
Returns 0 on success, -1 on any failure. π«Ά
void sdr_wm_usage(PsmUsageSummary *summary)
Loads summary with a snapshot of the usage of the SDR system's private working memory. To print the snapshot, use psm_report(). (See psm(3).) β¨
void sdr_shutdown( )
Ends all access to all SDRs (check sdr_stop_using()), detaches from the SDR system's working memory (releasing the memory if it was dynamically allocated by sdr_initialize()), and destroys the SDR system's private semaphore. After sdr_shutdown(), you must call sdr_initialize() again before any call to any other SDR library function. No cap. π
β¨ DATABASE ADMINISTRATION FUNCTIONS β¨
int sdr_load_profile(char *name, int configFlags, long heapWords, int memKey, char *pathName, char *restartCmd, unsigned int restartLatency)
Loads the profile for an SDR into the system's private list of SDR profiles. Here's the thing: SDRs themselves are persistent, but SDR profiles are not. So for an application to access an SDR, sdr_load_profile() must have been called to load the profile of the SDR since the last invocation of sdr_initialize(). Periodt.
nameis the name of the SDR, required for any subsequentsdr_start_using()call.configFlagsspecifies the configuration of the SDR, the bitwise "or" of some combination of the following:SDR_IN_DRAMβ SDR is implemented as a region of shared memory.SDR_IN_FILEβ SDR is implemented as a file.SDR_REVERSIBLEβ SDR transactions are logged and are reversed if canceled.SDR_BOUNDEDβ Heap updates are not allowed to cross object boundaries.
heapWordsspecifies the size of the heap in words; word size depends on machine architecture, i.e., a word is 4 bytes on a 32-bit machine, 8 bytes on a 64-bit machine. Note that each SDR prepends to the heap a "map" of predefined, fixed size. The total amount of space occupied by an SDR in memory and/or in a file is the sum of the size of the map plus the product of word size andheapWords.memKeyis ignored ifconfigFlagsdoes not includeSDR_IN_DRAM. It should normally beSM_NO_KEY, causing the shared memory region for the SDR to be allocated dynamically and shared using a dynamically selected shared memory key. If specified,memKeymust be a shared memory key identifying a pre-allocated region of shared memory whose length is equal to the total SDR size, shared via the indicated key.pathNameis ignored ifconfigFlagsincludes neitherSDR_REVERSIBLEnorSDR_IN_FILE. It is the fully qualified name of the directory into which the SDR's log file and/or database file will be written. The name of the log file (if any) will be"<sdrname>.sdrlog". The name of the database file (if any) will be"<sdrname>.sdr"; this file will be automatically created and filled with zeros if it does not exist at the time the SDR's profile is loaded.- If a cleanup task must be run whenever a transaction is reversed, the command to execute this task must be provided in
restartCmdand the number of seconds to wait for this task to finish before resuming operations must be provided inrestartLatency. IfrestartCmdisNULLorrestartLatencyis zero then no cleanup task will be run upon transaction reversal.
Returns 0 on success, -1 on any error.
int sdr_reload_profile(char *name, int configFlags, long heapWords, int memKey, char *pathName, char *restartCmd, unsigned int restartLatency)
Use this when the state of an SDR is thought to be inconsistent, maybe because a program crashed while it had a transaction open. It unloads the profile for the SDR, forcing the reversal of any transaction that is currently in progress when the SDR's profile is re-loaded. Then it calls sdr_load_profile() to re-load the profile for the SDR. Same return values as sdr_load_profile. π₯
Sdr sdr_start_using(char *name)
Locates SDR profile by name and returns a handle that can be used for all functions that operate on that SDR. On any failure, returns NULL. π
char *sdr_name(Sdr sdr)
Returns the name of the sdr. πΈ
long sdr_heap_size(Sdr sdr)
Returns the total size of the SDR heap, in bytes. π
void sdr_stop_using(Sdr sdr)
Terminates access to the SDR via this handle. Other users of the SDR are not affected. Frees the Sdr object. π
void sdr_abort(Sdr sdr)
Terminates the task. In flight configuration, also terminates all use of the SDR system by all tasks. π
void sdr_destroy(Sdr sdr)
Ends all access to this SDR, unloads the SDR's profile, and erases the SDR from memory and file system. π
β¨ DATABASE TRANSACTION FUNCTIONS β¨
void sdr_begin_xn(Sdr sdr)
Initiates a transaction. Note that transactions are single-threaded; any task that calls sdr_begin_xn() is suspended until all previously requested transactions have been ended or canceled. So don't be impatient, bestie. β³
int sdr_in_xn(Sdr sdr)
Returns 1 if called in the course of a transaction, 0 otherwise. Simple as that. β¨
void sdr_exit_xn(Sdr sdr)
Simply abandons the current transaction, ceasing the calling task's lock on ION. Must not be used if any database modifications were performed during the transaction; sdr_end_xn() must be called instead, to commit those modifications. Don't mess this up. π«
void sdr_cancel_xn(Sdr sdr)
Cancels the current transaction. If reversibility is enabled for the SDR, canceling a transaction reverses all heap modifications performed during that transaction. π
int sdr_end_xn(Sdr sdr)
Ends the current transaction. Returns 0 if the transaction completed without any error; returns -1 if any operation performed in the course of the transaction failed, in which case the transaction was automatically canceled. π
β¨ DATABASE I/O FUNCTIONS β¨
void sdr_read(Sdr sdr, char *into, Address from, int length)
Copies length characters at from (a location in the indicated SDR) to the memory location given by into. The data are copied from the shared memory region in which the SDR resides, if any; otherwise they are read from the file in which the SDR resides. π
void sdr_peek(sdr, variable, from)
sdr_peek() is a macro that uses sdr_read() to load variable from the indicated address in the SDR database; the size of variable is used as the number of bytes to copy. π
void sdr_write(Sdr sdr, Address into, char *from, int length)
Copies length characters at from (a location in memory) to the SDR heap location given by into. Can only be performed during a transaction, and if the SDR is configured for object bounding then heap locations into through (into + (length - 1)) must be within the extent of some object that was either allocated or staged within the same transaction. The data are copied both to the shared memory region in which the SDR resides, if any, and also to the file in which the SDR resides, if any. βοΈ
void sdr_poke(sdr, into, variable)
sdr_poke() is a macro that uses sdr_write() to store variable at the indicated address in the SDR database; the size of variable is used as the number of bytes to copy. π
char *sdr_pointer(Sdr sdr, Address address)
Returns a pointer to the indicated location in the heap β a "heap pointer" β or NULL if the indicated address is invalid. NOTE that this function cannot be used if the SDR does not reside in a shared memory region.
Providing an alternative to using sdr_read() to retrieve objects into local memory, sdr_pointer() can help make SDR-based applications run very quickly, but it must be used WITH GREAT CAUTION! Never use a direct pointer into the heap when not within a transaction, because you will have no assurance at any time that the object pointed to by that pointer has not changed (or is even still there). And NEVER de-reference a heap pointer in order to write directly into the heap: this makes transaction reversal impossible. Whenever writing to the SDR, always use sdr_write(). β οΈ
Address sdr_address(Sdr sdr, char *pointer)
Returns the address within the SDR heap of the indicated location, which must be (or be derived from) a heap pointer as returned by sdr_pointer(). Returns zero if the indicated location is not greater than the start of the heap mirror. NOTE that this function cannot be used if the SDR does not reside in a shared memory region. π§
void sdr_get(sdr, variable, heap_pointer)
sdr_get() is a macro that uses sdr_read() to load variable from the SDR address given by heap_pointer; heap_pointer must be (or be derived from) a heap pointer as returned by sdr_pointer(). The size of variable is used as the number of bytes to copy. π
void sdr_set(sdr, heap_pointer, variable)
sdr_set() is a macro that uses sdr_write() to store variable at the SDR address given by heap_pointer; heap_pointer must be (or be derived from) a heap pointer as returned by sdr_pointer(). The size of variable is used as the number of bytes to copy. π§
π HEAP SPACE MANAGEMENT FUNCTIONS π
okay bestie, this is the part where we actually get stuff into and out of the heap. buckle up π
Object sdr_malloc(Sdr sdr, unsigned long size)β grabs a block of space out of the indicated SDR's heap.sizeis how big the block is gonna be, and heads up: the max is 1/2 of the maximum address space size (so like2Gon a 32-bit machine, no cap). returns the block address if it ate, and zero if the block just couldn't be allocated πObject sdr_insert(Sdr sdr, char *from, unsigned long size)β callssdr_malloc()to get a block ofsizebytes, and if that works out, it callssdr_write()to copysizebytes of data from memory atfrominto the fresh block. returns the block address on success, zero if allocation flopped.Object sdr_stow(sdr, variable)β this one's a macro, not a function, okay? it usessdr_insert()to insert a copy ofvariableinto the database, and it takesvariable's size as the number of bytes to copy. cute, right? π«Άint sdr_object_length(Sdr sdr, Object object)β tells you how many bytes of heap space got allocated to the application data sitting atobject.void sdr_free(Sdr sdr, Object object)β frees the heap space thatobjectis occupying so it can be re-allocated later. bye girl πvoid sdr_stage(Sdr sdr, char *into, Object from, int length)β kinda likesdr_read(), it copieslengthcharacters fromfrom(a spot in the heap of the indicated SDR) to the memory locationinto. but unlikesdr_get(),sdr_stage()demands thatfrombe the address of some allocated object, not just any old location in the heap. and when you call it inside a transaction, it notifies the SDR library that this object might get updated later in the transaction β which lets the library grab the object's size for later, so it can validate any attempts to write into a location inside that object. iflengthis zero, SDR privately retrieves the object's size but copies none of its content into memory. sneaky but valid β¨long sdr_unused(Sdr sdr)β returns the number of bytes of heap space that hasn't been allocated to either the large or the small objects pool yet.void sdr_usage(Sdr sdr, SdrUsageSummary *summary)β loads thatSdrUsageSummarystructure with a lil snapshot of the SDR's usage status. andSdrUsageSummaryis defined as:
typedef struct
{
char sdrName[MAX_SDR_NAME + 1];
unsigned int sdrSize;
unsigned int smallPoolSize;
unsigned int smallPoolFreeBlockCount[SMALL_SIZES];
unsigned int smallPoolFree;
unsigned int smallPoolAllocated;
unsigned int largePoolSize;
unsigned int largePoolFreeBlockCount[LARGE_ORDERS];
unsigned int largePoolFree;
unsigned int largePoolAllocated;
unsigned int unusedSize;
} SdrUsageSummary;
void sdr_report(SdrUsageSummary *summary)β sends a printed summary of the SDR's usage status straight tostdout. receipts, but make it a report πint sdr_heap_depleted(Sdr sdr)β a Boolean girlie: returns1if the total available space in the SDR's heap (that's small pool free + large pool free + unused) is less than 1/16 of the total size of the heap. otherwise it returns zero. basically it's the "we're running out of room" alarm π¨
π HEAP SPACE USAGE TRACING π
so here's the tea: if SDR_TRACE is defined when the SDR source code gets compiled, the system bakes in built-in support for simple tracing of SDR heap space usage. heap space allocations get logged, and heap space deallocations get matched up to those logged allocations, "closing" them. this is so helpful for sniffing out heap space leaks and other kinds of SDR heap access problems, no cap π΅οΈββοΈ
int sdr_start_trace(Sdr sdr, int traceLogSize, char *traceLogAddress)β kicks off an episode of SDR heap space usage tracing.traceLogSizeis how many bytes of shared memory to use for logging trace activity, and the frequency with which "closed" trace log events have to be deleted varies inversely with how much memory you gave the trace log (iykyk).traceLogAddressis normallyNULL, which makes the trace system dynamically allocatetraceLogSizebytes of shared memory for trace logging; but if it's non-NULL, it's gotta point totraceLogSizebytes of shared memory that the application already pre-allocated for exactly this. returns0on success,-1on any failure.void sdr_print_trace(Sdr sdr, int verbose)β prints a cumulative trace report and a current usage report forsdr. ifverboseis zero, only exceptions get printed β notably trace log events that are still open, aka potential SDR heap space leaks π β otherwise all the activity in the trace log gets printed.void sdr_clear_trace(Sdr sdr)β deletes all the closed trace log events from the log, freeing up memory for more tracing. spring cleaning πΈvoid sdr_stop_trace(Sdr sdr)β ends the current episode of SDR heap space usage tracing. and if the shared memory used for the trace log was allocated bysdr_start_trace(), it releases that shared memory too. that's closure, bestie β¨
π CATALOGUE FUNCTIONS π
the SDR catalogue functions keep the catalogue of the names, types, and addresses of objects within an SDR. the catalogue service comes with functions for creating, deleting and finding catalogue entries, plus one for navigating through catalogue entries sequentially. it's giving librarian energy π
void sdr_catlg(Sdr sdr, char *name, int type, Object object)β associatesobjectwithnamein the indicated SDR's catalogue and notes thetypethat was declared for this object.typeis optional and means literally nothing beyond whatever the application decides it means.
heads up: the SDR catalogue is flat, not hierarchical like a directory tree, and all names must be unique. also the length of name is limited to 15 characters. keep it short, babe.
Object sdr_find(Sdr sdr, char *name, int *type)β finds theObjectassociated withnamein the indicated SDR's catalogue and returns its address; it also reports the catalogued type of the object in*typeiftypeis non-NULL. returns zero if nothing's currently catalogued under this name πvoid sdr_uncatlg(Sdr sdr, char *name)β dissociates fromnamewhatever object in the indicated SDR's catalogue is currently hanging out under that name.Object sdr_read_catlg(Sdr sdr, char *name, int *type, Object *object, Object previous_entry)β this is the one for navigating catalogue entries sequentially. ifprevious_entryis zero, it reads the first entry in the indicated SDR's catalogue; otherwise it reads the next catalogue entry following the one atprevious_entry. either way, it returns zero if no such catalogue entry exists; otherwise it copies that entry's name, type, and catalogued object address intoname,*type, and*object, and then returns the address of the catalogue entry β which you can totally use asprevious_entryin a later call tosdr_read_catlg(). chain them together, it's a whole vibe π¦
π USER'S GUIDE π
Compiling an SDR application
just make sure you "#include "sdr.h"" at the top of every source file that has any SDR function calls in it. that's it, that's the step.
for UNIX applications, link with -lsdr.
Loading an SDR application (VxWorks)
ld < "libsdr.o"
once the library is loaded, you can start loading SDR applications. slay π
π SEE ALSO π
sdrlist(3), sdrstring(3), sdrtable(3)
and that's a wrap on this whole page, bestie!! this one's from perl v5.24.1, dated 2016-07-07, and it's the ici::doc::pod3::sdr(3) man page β iconic, timeless, still valid. go forth and manage that heap. mwah ππβ¨
π¦ 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