blob: 2aafdca04539c55835d4d9cf207df9f4b9c7223b [file] [log] [blame]
/*
* Copyright (C) 2006-2010 Red Hat, Inc.
*
* Author: Steven Dake <sdake@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <qb/qbhdb.h>
#include <qb/qbatomic.h>
enum QB_HDB_HANDLE_STATE {
QB_HDB_HANDLE_STATE_EMPTY,
QB_HDB_HANDLE_STATE_PENDINGREMOVAL,
QB_HDB_HANDLE_STATE_ACTIVE
};
static void
qb_hdb_create_first_run(struct qb_hdb *hdb)
{
if (hdb->first_run == QB_TRUE) {
hdb->first_run = QB_FALSE;
qb_atomic_init();
hdb->handles = qb_array_create(32, sizeof(struct qb_hdb_handle));
}
}
void
qb_hdb_create(struct qb_hdb *hdb)
{
memset(hdb, 0, sizeof(struct qb_hdb));
hdb->first_run = QB_TRUE;
qb_hdb_create_first_run(hdb);
}
void
qb_hdb_destroy(struct qb_hdb *hdb)
{
qb_array_free(hdb->handles);
memset(hdb, 0, sizeof(struct qb_hdb));
}
int32_t
qb_hdb_handle_create(struct qb_hdb *hdb, int32_t instance_size,
qb_handle_t * handle_id_out)
{
int32_t handle;
int32_t res = 0;
int32_t check;
int32_t found = QB_FALSE;
void *instance;
int32_t i;
struct qb_hdb_handle *entry = NULL;
int32_t handle_count;
qb_hdb_create_first_run(hdb);
handle_count = qb_atomic_int_get(&hdb->handle_count);
for (handle = 0; handle < handle_count; handle++) {
if (qb_array_index(hdb->handles, handle, (void**)&entry) == 0 &&
entry->state == QB_HDB_HANDLE_STATE_EMPTY) {
found = QB_TRUE;
qb_atomic_int_inc(&entry->ref_count);
break;
}
}
if (found == QB_FALSE) {
res = qb_array_grow(hdb->handles, handle_count + 1U);
if (res != 0) {
return res;
}
res = qb_array_index(hdb->handles, handle_count,
(void **)&entry);
if (res != 0) {
return res;
}
/* NB: qb_array_grow above guarantees that handle_count
will not overflow INT32_MAX */
qb_atomic_int_inc((int32_t *)&hdb->handle_count);
}
instance = malloc(instance_size);
if (instance == 0) {
return -ENOMEM;
}
/*
* Make sure just positive integers are used for the integrity(?)
* checks within 2^32 address space, if we miss 200 times in a row
* (just 0 is concerned per specification of random), the PRNG may be
* broken -> the value is unspecified, subject of stack allocation.
*/
for (i = 0; i < 200; i++) {
check = random();
if (check > 0) {
break; /* covers also check == UINT32_MAX */
}
}
memset(instance, 0, instance_size);
entry->state = QB_HDB_HANDLE_STATE_ACTIVE;
entry->instance = instance;
entry->ref_count = 1;
entry->check = check;
*handle_id_out = (((uint64_t) (check)) << 32) | handle;
return res;
}
int32_t
qb_hdb_handle_get(struct qb_hdb * hdb, qb_handle_t handle_in, void **instance)
{
int32_t check = handle_in >> 32;
int32_t handle = handle_in & UINT32_MAX;
struct qb_hdb_handle *entry;
int32_t handle_count;
qb_hdb_create_first_run(hdb);
*instance = NULL;
handle_count = qb_atomic_int_get(&hdb->handle_count);
if (handle >= handle_count) {
return (-EBADF);
}
if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
entry->state != QB_HDB_HANDLE_STATE_ACTIVE) {
return (-EBADF);
}
if (check != (int32_t) UINT32_MAX && check != entry->check) {
return (-EBADF);
}
qb_atomic_int_inc(&entry->ref_count);
*instance = entry->instance;
return (0);
}
int32_t
qb_hdb_handle_get_always(struct qb_hdb * hdb, qb_handle_t handle_in,
void **instance)
{
return qb_hdb_handle_get(hdb, handle_in, instance);
}
int32_t
qb_hdb_handle_put(struct qb_hdb * hdb, qb_handle_t handle_in)
{
int32_t check = handle_in >> 32;
int32_t handle = handle_in & UINT32_MAX;
struct qb_hdb_handle *entry;
int32_t handle_count;
qb_hdb_create_first_run(hdb);
handle_count = qb_atomic_int_get(&hdb->handle_count);
if (handle >= handle_count) {
return (-EBADF);
}
if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
(check != (int32_t) UINT32_MAX && check != entry->check)) {
return (-EBADF);
}
if (qb_atomic_int_dec_and_test(&entry->ref_count)) {
if (hdb->destructor) {
hdb->destructor(entry->instance);
}
free(entry->instance);
memset(entry, 0, sizeof(struct qb_hdb_handle));
}
return (0);
}
int32_t
qb_hdb_handle_destroy(struct qb_hdb * hdb, qb_handle_t handle_in)
{
int32_t check = handle_in >> 32;
int32_t handle = handle_in & UINT32_MAX;
int32_t res;
struct qb_hdb_handle *entry;
int32_t handle_count;
qb_hdb_create_first_run(hdb);
handle_count = qb_atomic_int_get(&hdb->handle_count);
if (handle >= handle_count) {
return (-EBADF);
}
if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
(check != (int32_t) UINT32_MAX && check != entry->check)) {
return (-EBADF);
}
entry->state = QB_HDB_HANDLE_STATE_PENDINGREMOVAL;
res = qb_hdb_handle_put(hdb, handle_in);
return (res);
}
int32_t
qb_hdb_handle_refcount_get(struct qb_hdb * hdb, qb_handle_t handle_in)
{
int32_t check = handle_in >> 32;
int32_t handle = handle_in & UINT32_MAX;
struct qb_hdb_handle *entry;
int32_t handle_count;
int32_t refcount = 0;
qb_hdb_create_first_run(hdb);
handle_count = qb_atomic_int_get(&hdb->handle_count);
if (handle >= handle_count) {
return (-EBADF);
}
if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 ||
(check != (int32_t) UINT32_MAX && check != entry->check)) {
return (-EBADF);
}
refcount = qb_atomic_int_get(&entry->ref_count);
return (refcount);
}
void
qb_hdb_iterator_reset(struct qb_hdb *hdb)
{
hdb->iterator = 0;
}
int32_t
qb_hdb_iterator_next(struct qb_hdb *hdb, void **instance, qb_handle_t * handle)
{
int32_t res = -1;
uint64_t checker;
struct qb_hdb_handle *entry;
uint32_t handle_count;
handle_count = qb_atomic_int_get(&hdb->handle_count);
while (hdb->iterator < handle_count) {
res = qb_array_index(hdb->handles,
hdb->iterator,
(void **)&entry);
if (res != 0) {
break;
}
checker = (uint64_t) (entry->check);
*handle = (checker << 32) | hdb->iterator;
res = qb_hdb_handle_get(hdb, *handle, instance);
hdb->iterator += 1;
if (res == 0) {
break;
}
}
return (res);
}
uint32_t
qb_hdb_base_convert(qb_handle_t handle)
{
return (handle & UINT32_MAX);
}
uint64_t
qb_hdb_nocheck_convert(uint32_t handle)
{
uint64_t retvalue = ((uint64_t) UINT32_MAX) << 32 | handle;
return (retvalue);
}