Context
Table of Contents
1 Context handling
The context variable is a handle for the state of the library,
and is stored in a data structure which can't be seen outside of
the library. To simplify compatibility with other languages, the
pointer to the internal data structure is converted into a 64-bit
signed integer, defined in the qmckl_context
type.
A value of QMCKL_NULL_CONTEXT
for the context is equivalent to a
NULL
pointer.
typedef int64_t qmckl_context ; #define QMCKL_NULL_CONTEXT (qmckl_context) 0
An immutable context would have required to implement a garbage collector. To keep the library simple, we have chosen to implement the context as a mutable data structure, so it has to be handled with care.
By convention, in this file context
is a qmckl_context
variable
and ctx
is a qmckl_context_struct*
pointer.
1.1 Data structure
The context keeps a ``date'' that allows to check which data needs to be recomputed. The date is incremented when the electron coordinates are updated.
When a new element is added to the context, the functions qmcklcontextcreate, qmcklcontextdestroy and qmcklcontextcopy should be updated inorder to make deep copies.
A tag is used internally to check if the memory domain pointed by a pointer is a valid context. This allows to check that even if the pointer associated with a context is non-null, we can still verify that it points to the expected data structure.
#define VALID_TAG 0xBEEFFACE #define INVALID_TAG 0xDEADBEEF
The qmckl_context_check
function checks if the domain pointed by
the pointer is a valid context. It returns the input qmckl_context
if the context is valid, QMCKL_NULL_CONTEXT
otherwise.
qmckl_context qmckl_context_check(const qmckl_context context) ;
qmckl_context qmckl_context_check(const qmckl_context context) { if (context == QMCKL_NULL_CONTEXT) return QMCKL_NULL_CONTEXT; const qmckl_context_struct* const ctx = (const qmckl_context_struct* const) context; /* Try to access memory */ if (ctx->tag != VALID_TAG) { return QMCKL_NULL_CONTEXT; } return context; }
1.2 Creation
To create a new context, qmckl_context_create()
should be used.
- Upon success, it returns a pointer to a new context with the
qmckl_context
type - It returns
QMCKL_NULL_CONTEXT
upon failure to allocate the internal data structure - A new context always has all its members initialized with a NULL value
qmckl_context qmckl_context_create() { qmckl_context_struct* const ctx = (qmckl_context_struct* const) malloc (sizeof(qmckl_context_struct)); if (ctx == NULL) { return QMCKL_NULL_CONTEXT; } /* Set all pointers and values to NULL */ { memset(ctx, 0, sizeof(qmckl_context_struct)); } /* Initialize lock */ { pthread_mutexattr_t attr; int rc; rc = pthread_mutexattr_init(&attr); assert (rc == 0); (void) pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); rc = pthread_mutex_init ( &(ctx->mutex), &attr); assert (rc == 0); (void) pthread_mutexattr_destroy(&attr); } /* Initialize data */ { ctx->tag = VALID_TAG; const qmckl_context context = (const qmckl_context) ctx; assert ( qmckl_context_check(context) != QMCKL_NULL_CONTEXT ); qmckl_exit_code rc; ctx->numprec.precision = QMCKL_DEFAULT_PRECISION; ctx->numprec.range = QMCKL_DEFAULT_RANGE; rc = qmckl_init_electron(context); assert (rc == QMCKL_SUCCESS); rc = qmckl_init_nucleus(context); assert (rc == QMCKL_SUCCESS); rc = qmckl_init_ao_basis(context); assert (rc == QMCKL_SUCCESS); } /* Allocate qmckl_memory_struct */ { const size_t size = 128L; qmckl_memory_info_struct * new_array = calloc(size, sizeof(qmckl_memory_info_struct)); if (new_array == NULL) { free(ctx); return QMCKL_NULL_CONTEXT; } memset( &(new_array[0]), 0, size * sizeof(qmckl_memory_info_struct) ); ctx->memory.element = new_array; ctx->memory.array_size = size; ctx->memory.n_allocated = (size_t) 0; } return (qmckl_context) ctx; }
1.3 Locking
For thread safety, the context may be locked/unlocked. The lock is
initialized with the PTHREAD_MUTEX_RECURSIVE
attribute, and the
number of times the thread has locked it is saved in the
lock_count
attribute.
void qmckl_lock(qmckl_context context) { if (context == QMCKL_NULL_CONTEXT) return ; qmckl_context_struct* const ctx = (qmckl_context_struct* const) context; errno = 0; int rc = pthread_mutex_lock( &(ctx->mutex) ); if (rc != 0) { fprintf(stderr, "DEBUG qmckl_lock:%s\n", strerror(rc) ); fflush(stderr); } assert (rc == 0); ctx->lock_count += 1; /* printf(" lock : %d\n", ctx->lock_count); */ } void qmckl_unlock(const qmckl_context context) { qmckl_context_struct* const ctx = (qmckl_context_struct* const) context; int rc = pthread_mutex_unlock( &(ctx->mutex) ); if (rc != 0) { fprintf(stderr, "DEBUG qmckl_unlock:%s\n", strerror(rc) ); fflush(stderr); } assert (rc == 0); ctx->lock_count -= 1; /* printf("unlock : %d\n", ctx->lock_count); */ }
1.4 TODO Copy
qmckl_context_copy
makes a deep copy of a context. It returns
QMCKL_NULL_CONTEXT
upon failure.
qmckl_context qmckl_context_copy(const qmckl_context context) { const qmckl_context checked_context = qmckl_context_check(context); if (checked_context == QMCKL_NULL_CONTEXT) { return QMCKL_NULL_CONTEXT; } /* qmckl_lock(context); { const qmckl_context_struct* const old_ctx = (qmckl_context_struct* const) checked_context; qmckl_context_struct* const new_ctx = (qmckl_context_struct* const) malloc (context, sizeof(qmckl_context_struct)); if (new_ctx == NULL) { qmckl_unlock(context); return QMCKL_NULL_CONTEXT; } * Copy the old context on the new one * * TODO Deep copies should be done here * memcpy(new_ctx, old_ctx, sizeof(qmckl_context_struct)); qmckl_unlock( (qmckl_context) new_ctx ); return (qmckl_context) new_ctx; } qmckl_unlock(context); */ return QMCKL_NULL_CONTEXT; }
1.5 Destroy
The context is destroyed with qmckl_context_destroy
, leaving the ancestors untouched.
It frees the context, and returns the previous context.
qmckl_exit_code qmckl_context_destroy(const qmckl_context context) { const qmckl_context checked_context = qmckl_context_check(context); if (checked_context == QMCKL_NULL_CONTEXT) return QMCKL_INVALID_CONTEXT; qmckl_context_struct* const ctx = (qmckl_context_struct* const) context; assert (ctx != NULL); /* Shouldn't be possible because the context is valid */ qmckl_lock(context); { /* Memory: Remove all allocated data */ for (size_t pos = (size_t) 0 ; pos < ctx->memory.array_size ; ++pos) { if (ctx->memory.element[pos].pointer != NULL) { free(ctx->memory.element[pos].pointer); memset( &(ctx->memory.element[pos]), 0, sizeof(qmckl_memory_info_struct) ); ctx->memory.n_allocated -= 1; } } assert (ctx->memory.n_allocated == (size_t) 0); free(ctx->memory.element); ctx->memory.element = NULL; ctx->memory.array_size = (size_t) 0; } qmckl_unlock(context); ctx->tag = INVALID_TAG; const int rc_destroy = pthread_mutex_destroy( &(ctx->mutex) ); if (rc_destroy != 0) { /* DEBUG */ fprintf(stderr, "qmckl_context_destroy: %s (count = %d)\n", strerror(rc_destroy), ctx->lock_count); abort(); } free(ctx); return QMCKL_SUCCESS; }