General¶
General API Design, Error Handling¶
Nearly all C API functions in fluxEngine’s API return an int to
indicate whether an error has occurred or not. A return value 0
indicates that the function call was successful, while a return value
-1 indicates that the function call failed. There are some methods
that return a simple number, such as
fluxEngine_C_v1_ProcessingContext_num_output_sinks. In that case a
non-negative return value indicates that the function call was
successful (though the return value may be 0 to indicate that the
number is zero), while a value -1 still indicates an error condition.
Error Handling¶
All API functions that can return an error will have their final
parameter be fluxEngine_C_v1_Error** error. This allows the user
to specify a pointer to the opaque fluxEngine_C_v1_Error
structure to give some indication about the error that occurred:
1 2 3 4 5 6 7 8 | fluxEngine_C_v1_Error* error = NULL;
int rc = fluxEngine_C_v1_some_function(some parameters, &error);
if (rc < 0) {
printf("fluxEngine error: %s\n",
fluxEngine_C_v1_Error_get_message(error));
fluxEngine_C_v1_Error_free(error);
return;
}
|
If the user supplies a pointer to capture the error information, they
must free it with fluxEngine_C_v1_Error_free, or resources
will leak. However, if an API function returns 0 it is guaranteed
that the error pointer was not changed and nothing was allocated.
If the user specifies NULL for the error pointer, they can still detect that a function call failed, but they cannot determine by what reason:
1 2 3 4 5 | int rc = fluxEngine_C_v1_some_function(some parameters, NULL);
if (rc < 0) {
printf("fluxEngine error (unknown)\n");
return;
}
|
See the documentation of fluxEngine_C_v1_Error for further
details on what information may be extracted in the case of an error.
Complex Return Values¶
There are API functions that create a pointer to an internal structure
within fluxEngine. These return that pointer in their penultimate
parameter. Take, for example, the fluxEngine_C_v1_init function
that initializes fluxEngine. It has the following signature:
int fluxEngine_C_v1_init(void const* license_data, size_t license_data_size, fluxEngine_C_v1_Handle** handle, fluxEngine_C_v1_Error** error)
This follows the standard error handling pattern described above.
Additionally it has another output parameter handle that will be
filled with the pointer to the newly allocated handle on success.
This may be called in the following manner:
1 2 3 4 5 6 7 8 9 10 11 12 | fluxEngine_C_v1_Error* error = NULL;
fluxEngine_C_v1_Handle* handle = NULL;
int rc = fluxEngine_C_v1_init(license_data, license_data_size, &handle, &error);
if (rc < 0) {
printf("fluxEngine error: %s\n",
fluxEngine_C_v1_Error_get_message(error));
fluxEngine_C_v1_Error_free(error);
return;
}
// From this point on handle contains a valid pointer
// At the end: free the handle
fluxEngine_C_v1_destroy(handle);
|
Initializing the Library¶
To initialize the library a license file is required. The user must read that license file into memory and supply fluxEngine with it. See read_file for example code for reading the license.
The following code demonstrates how to properly initialize fluxLicense and how to tear it down again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | fluxEngine_C_v1_Error* error = NULL;
fluxEngine_C_v1_Handle* handle = NULL;
int rc = 0;
void* license_data = NULL;
size_t license_data_size = 0;
/* Read the entire license data from disk
* (see the helper code in the documentation for a possible
* implementation of this method)
*/
license_data = read_file("fluxEngine.lic", &license_data_size);
if (!license_data) {
fprintf(stderr, "Could not read license file: %s\n", strerror(errno));
return;
}
rc = fluxEngine_C_v1_init(license_data, license_data_size,
&handle, &error);
/* Once fluxLicense has been initialized, we do not need
* the license data anymore. */
free(license_data);
if (rc != 0) {
fprintf(stderr, "Could not initialize fluxEngine: %s\n",
fluxEngine_C_v1_Error_get_message(error));
fluxEngine_C_v1_Error_free(error);
return;
}
/*
******************************************************************
* Do something with fluxLicense here.
******************************************************************
*/
/* At the end: free resources */
fluxEngine_C_v1_destroy(handle);
|
Setting up processing threads¶
fluxEngine supports parallel processing, but it has to be set up at the
very beginning. The eastiest method to do so is via the
fluxEngine_C_v1_create_processing_threads function.
The following example code demonstrates how to perform processing with 4 threads:
1 2 3 4 5 6 7 8 9 | fluxEngine_C_v1_Error* error = NULL;
int rc = 0;
rc = fluxEngine_C_v1_create_processing_threads(handle, 4, &error);
if (rc != 0) {
fprintf(stderr, "Could not setup fluxEngine processing threads: %s\n",
fluxEngine_C_v1_Error_get_message(error));
fluxEngine_C_v1_Error_free(error);
return;
}
|
Note
This will only create 3 (not 4!) background threads that will
help with data processing. The thread that calls
fluxEngine_C_v1_ProcessingContext_process_next will be
considered the first thread (with index 0) that participates
in parallel processing.
Note
Modern processors support Hyperthreading (Intel) or SMT (AMD) to provide more logical cores that are phyiscally available. It is generally not recommended to use more threads than are phyiscally available, as workloads such as fluxEngine will typically slow down when using more cores in a system than are physically available.
Note
When running fluxEngine with very small amounts of data, in the extreme case with cubes that have only one pixel, parallelization will not improve performance. In cases where cubes consisting of only one pixel are processed, it is recommended to not parallelize at all and skip this step.
Note
Only one fluxEngine operation may be performed per handle at the same time; executing multiple processing contexts from different threads will cause them to be run sequentially.
Since it is currently possible to only create a single handle for fluxEngine, this means only one operation can be active at the same time; though the limitation of only a single handle will be lifted in a later version of fluxEngine.