Broadcast Component

The BCast object is an any to many payload broadcaster.

A BCast object is used for synchronization and communication from a process to one or more processes. Unlike channels, it is not designed to be used in synchronization/communication between two processes, which could be done using a channel and by sending a message from a sender to a receiver. Using a BCast object, many processes can wait on the BCast object until a process has notified them that a payload is available via a call to a trigger function. The payload is optional. The BCast object provides an efficient any to many synchronization/communication structure.

../_images/bcast.png

Fig. 69 An Any to Many Broadcast Synchronization Object

A BCast object is meant to be shared by multiple threads/processes. The object is first created by a process. Then a serialized descriptor to it can be shared with other processes. Via this serialized descriptor other processes may attach to the same BCast Object. So there is one create call and perhaps many attach calls using the API below. When the BCast object is no longer needed, all processes should detach or destroy object. The destroy API call should only be called once per object.

The object can be triggered to provide its payload to waiters. There are four options for waiting.

  • Idle Wait - A process sleeps via a futex until it is triggered via a syscall. This is relatively expensive, but has the advantage of completely suspending the process.

  • Spin Wait - A process sleeps by looping while taking advantage of any processor specific ability to relenquish cycles to other processes while it it spinning.

  • Asynchronously Wait for a callback - The process continues executing and a thread handles the callback.

  • Asynchronously Wait for a signal - A signal handler for the signal must be installed by the programmer.

When creating a BCast object, the programmer must decide on the maximum sized payload that could be provided to waiters. The programmer must also decide on the maximum number of spin waiters that will be allowed to spin on its trigger event.

Triggering processes may trigger one or all processes that are waiting on a BCast object.

  • When triggering occurs, all new waiters must wait until triggering is complete.

  • Only current waiters will be triggered.

  • The triggering process provides the payload to be distributed when trigger is called.

  • The triggering process wakes up all waiters or one waiter.

  • Each triggered waiter process returns from its BCast wait primitive, but before it returns it copies the payload from the BCast object into the heap of the waiting process and provides a pointer to the payload to the triggered waiting process.

../_images/bcastflow.srms1.png

Fig. 70 Operations on a BCast Object

The flow diagram in Fig. 70 shows an interaction with a BCast object and points out a few features/semantics of these synchronization/communication objects. The flow of interaction proceeds as follows:

  1. The process T1 creates the BCast object and through some means, communicates its location to all the other processes in this interaction.

  2. In step 2 all the waiter processes begin to wait on the BCast object.

  3. In step 3 Trigger One is called before W4 can initiate its wait. W4 must wait until triggering is complete.

  4. At step 4 W1, W3, and W4 are waiters and Trigger All is called. The payload will be copied into the local heap of all the processes.

  5. While triggering is still happening, W2 wants to wait, but it will not be a waiter until triggering is complete.

  6. It is also possible to sign up for an asynchronous notification via either a callback or a signal. In this step, the process W3 signs up for a callback. The callback is called as a thread under the W3 process.

  7. When Trigger All is called the callback to cb2 is initiated in a thread of process W3. Process W2 is unblocked as well.

  8. When Trigger One is called by T4 there are no waiters on the object. Without a waiter, the Trigger call is invalid and is rejected.

Client API

This section documents the user-level C API.

Structures

Descriptors

At the user level there are two descriptor types that are used in interacting with a BCast object. The dragonBCastDescr_t structure is an opaque handle to a BCast object. All interaction with a BCast object takes place through an instance of this descriptor that has been initialized by calling one of the create functions.

struct dragonBCastDescr_t

A BCast handle.

The internals of this structure should not be used directly. It provides an opaque handle to a BCast object. All manipulation of the BCast occurs via its API functions. A process gets a valid dragonBCastDescr_t handle by either creating or attaching to a BCast object using the same API calls.

A dragonBCastSerial_t structure is the descriptor for a serialized representation of the object. A serialized representation can only be created for a BCast object created in managed memory with the dragon_bcast_create call.

struct dragonBCastSerial_t

A serialized BCast handle.

This structure should not be used directly. All manipulation of the BCast object occurs via its API functions. A process gets a valid serialized descriptor by calling serialize on a valid BCast object.

The data is a serialized representation of a BCast object that can be passed between processes/thread and used to attach to the same object in another process or thread.

Public Members

size_t len

The length of the serialized data

uint8_t *data

A pointer to the serialized data

Attributes

Broadcast attributes may be specified when the BCast object is created.

enum dragonSyncType_t

Constants to specify synchronization between waiters and triggerers.

These constants are provided as part of the BCast attributes when a BCast is created. The default value of DRAGON_NO_SYNC means it is up to the application to decide on when triggering and waiting occur. When DRAGON_SYNC is specified, then a triggerer will wait until at least one waiter is waiting, providing synchronization between triggerers and waiters. The number of required waiters is provided in the BCast attributes.

Values:

enumerator DRAGON_NO_SYNC

When specified, triggering processes will never block waiting for waiters. This is the default.

enumerator DRAGON_SYNC

When specified, a triggerer will block, waiting for waiters on the BCast object. The required number of waiters is specified in the attributes of the BCast.

struct dragonBCastAttr_t

BCast Attributes

This structure defines user selectable attributes of the BCast Synchronization object. The lock type used internally may be user specified. The sync_type is as described in dragonSyncType_t. If DRAGON_SYNC is specified, then the user is guaranteed that no trigger events will occur without a waiter. This is at the expense of the triggerer blocking should no waiter be presently waiting on the bcast, but provides a convenient synchronization mechanism while eliminating the possibility of a trigger event going unnoticed. The default is NO_SYNC for no synchronization between waiters and triggerers.

Public Members

dragonLockKind_t lock_type

The lock type used within the BCast object

dragonSyncType_t sync_type

The synchronization requirement

dragonULInt sync_num

Only valid when DRAGON_SYNC is specified. This specifies the number of waiters that must be waiting before a trigger can occur.

The Handle

This is an internal handle, used internally only in interacting with the BCast object. It is materialized within API calls by using the dragonBCastDescr_t descriptor to look up the handle in a umap structure for BCast objects. In this way descriptor objects are completely opaque.

struct dragonBCast_t

API

These are the user-facing API calls for BCast objects.

dragonError_t dragon_bcast_size(size_t max_payload_sz, size_t max_spinsig_num, dragonBCastAttr_t *attr, size_t *size)

Compute the size of space needed to host a bcast object.

When creating a bcast synchronization object in pre-allocated memory, this function returns the required size for a given payload maximum size and a maximum number of spin/async notification waiters. The maximum number of waiters using either spin wait or asynchronous signal notification must be known when this function is called to compute the required memory size.

You must call dragon_bcast_attr_init to initialize attr before calling this function unless you want all the defaults. If you want default behavior, attr may be NULL.

Parameters
  • max_payload_sz – The maximum number of bytes that will be needed to store the payload to be broadcast to others.

  • max_spinsig_num – The maximum number of spinner waiters and async waiters that will wait on this BCast object.

  • attr – The other attributes of the object. Some of these may impact the size.

  • size – A pointer to a size_t variable to hold the required size. This will be the required number of bytes needed to hold this object in memory.

Returns

DRAGON_SUCCESS or an error code indicating the problem.

Example Usage

size_t sz;
dragonError_t err;
err = dragon_bcast_size(256, 10, NULL, &sz);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_attr_init(dragonBCastAttr_t *attr)

Initialize a BCast Attributes Structure.

When creating a bcast synchronization object certain attributes may be supplied to affect how the object behaves. This attr_init function initializes the attributes structure to default values which then may be subsequently overridden before creating the BCast object.

Parameters

attr – A pointer to the attribute structure for a BCast object.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

dragonBCastAttr_t attr;
/* initialize an BCast attributes structure
   to default attributes */
dragon_bcast_attr_init(&attr);

dragonError_t dragon_bcast_create_at(void *loc, size_t alloc_sz, size_t max_payload_sz, size_t max_spinsig_num, dragonBCastAttr_t *attr, dragonBCastDescr_t *bd)

Create a BCast object in pre-allocated memory for use in sharing a payload.

Create a BCast object in the pre-allocated memory pointed to by loc, with max_payload_sz and initialize the handle to it in bd. BCast objects are on-node only objects. The max_payload_sz must be greater than or equal to 0. The memory in which the BCast object is to be created must start on a word boundary. Four byte (i.e. word) boundary alignment is required.

Parameters
  • loc – The location in memory where this BCast object will be located.

  • alloc_sz – The size of the allocation pointed to by loc.

  • max_payload_sz – The maximum size payload to be allowed for this object.

  • max_spinsig_num – The maximum number of spin/async notification waiters on this object.

  • attr – Attributes that control the behavior of this object.

  • bd – The BCast’s descriptor handle.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

dragonError_t err;
size_t sz;
err = dragon_bcast_size(256, 10, NULL, &sz);
if (err != DRAGON_SUCCESS) {
    // take some action
}
void* ptr = malloc(sz);
err = dragon_bcast_create_at(ptr, 256, 10, NULL, &bd);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_create(dragonMemoryPoolDescr_t *pd, size_t max_payload_sz, size_t max_spinsig_num, dragonBCastAttr_t *attr, dragonBCastDescr_t *bd)

Create a BCast object in a managed memory pool for use in sharing a payload.

Create a BCast object in the given memory pool, with max_payload_sz and initialize the handle to it in bd. BCast objects are on-node only objects. The max_payload_sz must be greater than or equal to 0.

Parameters
  • pd – A pool descriptor where the BCast object will be created.

  • max_payload_sz – The maximum size payload to be allowed for this object.

  • max_spinsig_num – The maximum number of spin/async notification waiters on this object.

  • attr – Attributes that control the behavior of this object.

  • bd – The BCast’s descriptor handle.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

dragonMemoryPoolDescr_t pool;
// create a memory pool and initialize the descriptor
dragonBCastDescr_t bd;
dragonError_t err;

// create in memory pool
err = dragon_bcast_create(&pool, 128, 10, NULL, &bd);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_destroy(dragonBCastDescr_t *bd)

Destroy a BCast object.

When done using a BCast object, destroy should be called once to release the pool resources used by the object.

Parameters

bd – The BCast’s descriptor handle.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

dragonMemoryPoolDescr_t pool;
// create a memory pool and initialize the descriptor
dragonBCastDescr_t bd;
dragonError_t err;

// create in memory pool
err = dragon_bcast_create(&pool, 128, 10, NULL, &bd);
if (err != DRAGON_SUCCESS) {
    // take some action
}

// use it then destroy it.
err = dragon_bcast_destroy(&bd);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_attach(dragonBCastSerial_t *serial, dragonBCastDescr_t *bd)

Attach to a BCast Object.

A serialized descriptor can be shared between processes. A process may attach to a BCast Object using one of these serialized descriptors.

Parameters
  • bd_ser – The serialized BCast object descriptor.

  • bd – A BCast descriptor handle to be initialized by this call.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

dragonBCastDescr_t bd, bd2;
dragonBCastSerial_t bd_ser;
dragonError_t err;

// create in memory pool
err = dragon_bcast_create(&pool, 128, 10, NULL, &bd);

check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);

if (err != DRAGON_SUCCESS) {
    // take some action
}

err = dragon_bcast_serialize(&bd, &bd_ser);

if (err != DRAGON_SUCCESS) {
    // take some action
}

// presumably someplace where we don't have access
// to bd.
err = dragon_bcast_attach(&bd_ser, &bd2);

if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_detach(dragonBCastDescr_t *bd)

Detach from a BCast object.

When done using a BCast object, detach can be called to end the use of the BCast’s handle in the current process. Calling detach does not release any of the shared resources of this object but will release any resources local to this process.

Parameters

bd – The BCast’s descriptor handle.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

err = dragon_bcast_detach(&bd);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_serialize(const dragonBCastDescr_t *bd, dragonBCastSerial_t *bd_ser)

Serialize a BCast Object.

When sharing a BCast object, a serialied descriptor can be created by calling this function. This serialized descriptor contains data that when shared can be used to attach to the BCast object from another process. This function is only valid to call on a BCast object created via the create function call. The create_at api call does not support serialization.

Parameters
  • bd – The BCast’s descriptor handle.

  • bd_ser – The serialized descriptor created/initialized by this call.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

err = dragon_bcast_serialize(&bd, &bd_ser);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_serial_free(dragonBCastSerial_t *bd_ser)

Free a BCast serialized descriptor.

When a BCast serialized descriptor is no longer needed, it should be freed. Calling this function frees the resources used by this serialized descriptor only which does not free a serialized descriptor shared with other processes and does not free the underlying SyncObject’s resources.

Parameters

bd_ser – A BCast serialized descriptor.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example Usage

err = dragon_bcast_serial_free(&bd_ser);
if (err != DRAGON_SUCCESS) {
    // take some action
}

dragonError_t dragon_bcast_wait(dragonBCastDescr_t *bd, dragonWaitMode_t wait_mode, const timespec_t *timer, void **payload, size_t *payload_sz, dragonReleaseFun release_fun, void *release_arg)

Wait for the BCast object to be triggered.

Perform a wait on the BCast object specified by handle bd. The calling process will block until it is triggered by a call to one of the two trigger functions or until the timeout expires. If timeout is NULL then the process waits indefinitely.

Waiting can fail for a few reasons. If spin wait is specified and there are more spin waiters than the allowable configured maxium, this call will fail. If this is a synchronized BCast and sync_num processes are already waiting on this BCast, then this call will fail. In all cases, if the call fails an appropriate return code is returned to the caller and should be checked.

Parameters
  • bd – The BCast descriptor handle.

  • wait_mode – Either DRAGON_IDLE_WAIT or DRAGON_SPIN_WAIT. Idle waiting is more resource friendly, while spin waiting has better performance when there are enough cores to be dedicated to spin waiting. There is a fixed size limit to the number of spin waiters. If that value is exceeded, the BCast will revert to idle waiting until more spin wait slots open up. Preference is given to waking up spin waiters first. See dragon_bcast_trigger_one or trigger_all.

  • timer – is the timeout value. If it is NULL then spin wait indefinitely.

  • payload – is a pointer to where a pointer will be stored for the payload. This will refer to space that was allocated on the heap for the calling process and must be freed once the process is done with the payload.

  • payload_sz – is a pointer to the variable where the actual payload size will stored.

  • release_fun – If NULL is provided, this argument is ignored. Otherwise, it is a pointer to a function that takes one argument. Any return value of the function will be ignored. This function will be called once the waiter has become an official waiter of the BCast object or if the wait has to return prematurely because of an error.

  • release_arg – If release_fun is not NULL, then this is passed as an an argument to the release_fun function. The type of this argument is assumed to be a pointer to something.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.


dragonError_t dragon_bcast_notify_callback(dragonBCastDescr_t *bd, void *user_def_ptr, const dragonWaitMode_t wait_mode, const timespec_t *timer, dragonReleaseFun release_fun, void *release_arg, dragonBCastCallback cb)

Request an asynchronous notification when one of the BCast object’s trigger functions is called via a callback. The two arguments to this function are the BCast descriptor and a pointer to a callback function. The callback function should take five arguments as follows.

void callback(dragonBCastDescr_t* bd, void* payload, size_t payload_sz, dragonError_t rc, char* err_str)
The BCast descriptor pointer is the first argument to the callback. A payload pointer is the second argument followed by the size of the payload as the third argument. Finally, the last two are the return code, indicating success or not, and the error string if not successful (NULL otherwise).

Parameters
  • bd – The BCast’s descriptor handle.

  • user_def_ptr – This is a user defined pointer, presumably to something that the callback needs to complete its work. It is passed to the callback as the first argument. It can be any pointer that the user desires including the dragonBCastDescr_t pointer.

  • wait_mode – The type of waiting (.e.g SPIN_WAIT or IDLE_WAIT) that should be used.

  • timer – is the timeout value. If it is NULL then wait indefinitely to call the callback.

  • release_fun – is a function to call that will release some lock or resource after the callback has been registered as a waiter with the bcast. The release fun must take one argument, a void* pointer.

  • release_arg – is an argument to pass to the release_fun when it is called.

  • cb – The callback function to be called. The callback function signature must be as described in this functions description.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.


dragonError_t dragon_bcast_notify_signal(dragonBCastDescr_t *bd, const dragonWaitMode_t wait_mode, const timespec_t *timer, dragonReleaseFun release_fun, void *release_arg, int sig, void **payload_ptr, size_t *payload_sz, dragonError_t *drc, char **err_string)

Request an asynchronous signal when one of the BCast object’s trigger functions is called.

The asynchronous signal can come in one of two forms. This function defines a signal to occur when a trigger event occurs. The payload_ptr pointer and payload_sz pointer will be used to initialize a pointer to the payload and the size of the payload. The drc pointer will refer to the return code indicating the success of the asynchronous notification.

Parameters
  • bd – The BCast’s descriptor handle.

  • timer – is the timeout value. If it is NULL then wait indefinitely to signal the caller.

  • payload_ptr – The location in which to store a pointer to the payload when signal notification is requested.

  • payload_sz – The location in which to store the payload size when signal notification is requested.

  • rc – The location in which to store the asynchronous wait Dragon return code when signal notification is requested.

  • err_string – A pointer to a string pointer. Upon return it will hold an error string if the the rc indicates that there was an error. The err_string must be freed once the application is done with it.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.


dragonError_t dragon_bcast_trigger_one(dragonBCastDescr_t *bd, const timespec_t *timeout, const void *payload, const size_t payload_sz)

Unblock one blocked process waiting on this BCast bbject.

Triggers one blocked process waiting on this BCast object. If there is no payload, the payload pointer must be NULL and the payload_sz must be 0. Otherwise, the payload_sz bytes are copied from the address in payload to the object’s shared payload area so it is available to any processes waiting on this object.

Preference is given to trigger a spin waiter if one is available. If no spin waiters are waiting, then an idle waiter is triggered.

If the BCast object is synchronized, this method cannot be called to trigger just one waiter. On a synchronized BCast, only dragon_bcast_trigger_all may be called, even if the sync_num is set to 1 for the BCast.

If a process is waiting asynchronously and chosen by this call, a signal will be initiated or a callback will be called, depending on how the asynchronous notification was requested.

When DRAGON_SUCCESS is returned, this call successfully blocked until one waiting processes has picked up its payload.

Parameters
  • bd – The BCast’s descriptor handle.

  • timer – A timeout value. If the triggered process does not complete its triggering within the timeout period, this process will get a DRAGON_TIMEOUT return code. A value of NULL means to wait forever for triggering to complete.

  • payload – A pointer to the payload the triggering process wants to disseminate.

  • payload_sz – The size of the payload to be disseminated.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.


dragonError_t dragon_bcast_trigger_all(dragonBCastDescr_t *bd, const timespec_t *timeout, const void *payload, const size_t payload_sz)

Unblock all blocked processes waiting on this BCast bbject.

Triggers all blocked processes waiting on this BCast object. If there is no payload, the payload pointer must be NULL and the payload_sz must be 0. Otherwise, the payload_sz bytes are copied from the address in payload to the object’s shared payload area so it is available to any processes waiting on this object.

If the BCast object is synchronized, this call will block until there are sync_num number of waiters waiting on the BCast as specifed in the attributes when the BCast was created.

If waiting processes are waiting asynchronously, a signal will be initiated or a callback will be called, depending on how the asynchronous notification was requested.

When DRAGON_SUCCESS is returned, this call successfully blocked until all waiting processes had picked up their payloads.

Parameters
  • bd – The BCast’s descriptor handle.

  • timer – A timeout value. If all triggered processes do not complete triggering within the timeout period, this process will get a DRAGON_TIMEOUT return code. A value of NULL means to wait forever for triggering to complete.

  • payload – A pointer to the payload the triggering process wants to disseminate.

  • payload_sz – The size of the payload to be disseminated.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.


dragonError_t dragon_bcast_num_waiting(dragonBCastDescr_t *bd, int *num_waiters)

Get the number of waiting processes on this BCast object.

Return the number of waiting processes on this BCast object.

Parameters
  • bd – The BCast’s descriptor handle.

  • num_waiters – A pointer to an integer variable where the number waiting will be stored.

Returns

DRAGON_SUCCESS or a return code to indicate what problem occurred.

Example

This creates a BCast object. FIXME - This example is not complete. Placeholder only.

Listing 55 A BCast Example
  1#include <dragon/bcast.h>
  2#include <dragon/return_codes.h>
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <time.h>
  6
  7#define TRUE 1
  8#define FALSE 0
  9
 10#define SERFILE "bcast_serialized.dat"
 11#define MFILE "bcast_test"
 12#define M_UID 0
 13
 14#define FAILED 1
 15#define SUCCESS 0
 16
 17int create_pool(dragonMemoryPoolDescr_t* mpool) {
 18    /* Create a memory pool to allocate messages and a Channel out of */
 19    size_t mem_size = 1UL<<31;
 20    printf("Allocating pool of size %lu bytes.\n", mem_size);
 21
 22    dragonError_t derr = dragon_memory_pool_create(mpool, mem_size, MFILE, M_UID, NULL);
 23    if (derr != DRAGON_SUCCESS) {
 24        char * errstr = dragon_getlasterrstr();
 25        printf("Failed to create the memory pool.  Got EC=%i\nERRSTR = \n%s\n",derr, errstr);
 26        return FAILED;
 27    }
 28
 29    return SUCCESS;
 30}
 31
 32void check_result(dragonError_t err, dragonError_t expected_err, int* tests_passed, int* tests_attempted) {
 33    (*tests_attempted)++;
 34
 35    if (err != expected_err) {
 36        printf("Test %d Failed with error code %s\n", *tests_attempted, dragon_get_rc_string(err));
 37        printf("%s\n", dragon_getlasterrstr());
 38    }
 39    else
 40        (*tests_passed)++;
 41}
 42
 43int main(int argc, char* argv[]) {
 44
 45    timespec_t t1, t2;
 46    int tests_passed = 0;
 47    int tests_attempted = 0;
 48    dragonMemoryPoolDescr_t pool;
 49
 50    if (create_pool(&pool) != SUCCESS) {
 51        printf("Could not create memory pool for bcast tests.\n");
 52        return FAILED;
 53    }
 54
 55    dragonBCastDescr_t bd, bd2;
 56    dragonBCastSerial_t bd_ser;
 57
 58    dragonError_t err;
 59
 60    // create in memory pool
 61    err = dragon_bcast_create(&pool, 128, 10, NULL, &bd);
 62
 63    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
 64
 65    // destroy from memory pool
 66    err = dragon_bcast_destroy(&bd);
 67
 68    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
 69
 70    // destroy already destroyed bcast object
 71    err = dragon_bcast_destroy(&bd);
 72
 73    check_result(err, DRAGON_MAP_KEY_NOT_FOUND, &tests_passed, &tests_attempted);
 74
 75    size_t sz;
 76    err = dragon_bcast_size(256, 10, NULL, &sz);
 77
 78    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
 79
 80    tests_attempted++;
 81
 82    if (sz < 256)
 83        printf("Test %d Failed. The required size was too small.\n", tests_attempted);
 84    else
 85        tests_passed++;
 86
 87    void* ptr = malloc(sz);
 88
 89    err = dragon_bcast_create_at(ptr, 256, 10, NULL, &bd);
 90
 91    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
 92
 93    err = dragon_bcast_serialize(&bd, &bd_ser);
 94
 95    check_result(err, DRAGON_BCAST_NOT_SERIALIZABLE, &tests_passed, &tests_attempted);
 96
 97    err = dragon_bcast_destroy(&bd);
 98
 99    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
100
101    // create in memory pool
102    err = dragon_bcast_create(&pool, 128, 10, NULL, &bd);
103
104    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
105
106    err = dragon_bcast_serialize(&bd, &bd_ser);
107
108    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
109
110    err = dragon_bcast_attach(&bd_ser, &bd2);
111
112    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
113
114    err = dragon_bcast_detach(&bd2);
115
116    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
117
118    // This won't succeed because detaching the same BCast object just prior
119    // to this call, removes it from the umap. You can't detach and destroy
120    // the same BCast object.
121    err = dragon_bcast_destroy(&bd);
122
123    check_result(err, DRAGON_MAP_KEY_NOT_FOUND, &tests_passed, &tests_attempted);
124
125    err = dragon_bcast_attach(&bd_ser, &bd2);
126
127    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
128
129    err = dragon_bcast_serial_free(&bd_ser);
130
131    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
132
133    err = dragon_bcast_destroy(&bd2);
134
135    check_result(err, DRAGON_SUCCESS, &tests_passed, &tests_attempted);
136
137    dragon_memory_pool_destroy(&pool);
138
139    printf("Passed %d of %d tests.\n", tests_passed, tests_attempted);
140
141    return 0;
142}