Click here for the API reference documentation.
Usage
Multithreading
Sample Code
The term "reference" is used to mean "opaque data that refers to some conceptual object". It is intentionally vague to support "information hiding". Behind the scenes, different implementations can use almost anything that fits into a pointer as a "reference". Often, they are indexes into arrays or actual pointers to memory objects. When passing those references through an API to outside clients, the implementation becomes exposed to crash bugs when clients pass those references back into the API damaged or stale ("stale" meaning something that has been deleted).
Safe References are designed to help protect against damaged or stale references being used by clients.
Here's the basic usage pattern for safe references:
Client calls an API's "Create" function:
Followed by:
Client calls another API function, passing in the Safe Reference:
Finishing with:
Client calls API's "Delete" function, passing in the Safe Reference:
At this point, if the Client calls an API function and passes that same (now invalid) Safe Reference (or if the client accidentally passes in some garbage value, like a pointer or zero), the API function will try to translate that into an object pointer. But it'll be told that it's an invalid Safe Reference. The API function can then handle it gracefully, rather than just acting as if it were a valid reference and clobbering the object's deallocated memory or some other object that's reusing the old object's memory.
A Reference Map object is used to create Safe References and keep track of the mappings from Safe References to pointers. At start-up, a Reference Map is created by calling le_ref_CreateMap()
. It takes a single argument, the maximum number of mappings expected to track of at any time.
This API's functions are reentrant, but not thread safe. If there's the slightest possibility the same Reference Map will be accessed by two threads at the same time, use a mutex or some other thread synchronization mechanism to protect the Reference Map from concurrent access.
// Opaque reference to Foo objects. typedef struct xyz_foo_Obj* xyz_foo_ObjRef_t; xyz_foo_Ref_t xyz_foo_CreateObject ( void ); void xyz_foo_DoSomething ( xyz_foo_Ref_t objRef ); void xyz_foo_DeleteObject ( xyz_foo_Ref_t objRef );
// Maximum number of Foo objects we expect to have at one time. #define MAX_FOO_OBJECTS 27 // Actual Foo objects. typedef struct { ... } Foo_t; // Pool from which Foo objects are allocated. le_mem_PoolRef_t FooPool; // Safe Reference Map for Foo objects. le_ref_MapRef_t FooRefMap; COMPONENT_INIT { // Create the Foo object pool. FooPool = le_mem_CreatePool("FooPool", sizeof(Foo_t)); le_mem_ExpandPool(FooPool, MAX_FOO_OBJECTS); // Create the Safe Reference Map to use for Foo object Safe References. FooRefMap = le_ref_CreateMap("FooMap", MAX_FOO_OBJECTS); }; xyz_foo_Ref_t xyz_foo_CreateObject ( void ) { Foo_t* fooPtr = le_mem_ForceAlloc(FooPool); // Initialize the new Foo object. ... // Create and return a Safe Reference for this Foo object. return le_ref_CreateRef(FooRefMap, fooPtr); } void xyz_foo_DoSomething ( xyz_foo_Ref_t objRef ) { Foo_t* fooPtr = le_ref_Lookup(FooRefMap, objRef); if (fooPtr == NULL) { LE_CRIT("Invalid reference (%p) provided!", objRef); return; } // Do something to the object. ... } void xyz_foo_DeleteObject ( xyz_foo_Ref_t objRef ) { Foo_t* fooPtr = le_ref_Lookup(FooRefMap, objRef); if (fooPtr == NULL) { LE_CRIT("Invalid reference (%p) provided!", objRef); return; } // Invalidate the Safe Reference. le_ref_DeleteRef(FooRefMap, objRef); // Release the Foo object. le_mem_Release(fooPtr); }
Copyright (C) Sierra Wireless, Inc. 2013. All rights reserved. Use of this work is subject to license.