/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved.
*
* $Id$
*/
/*
* This is a template for allocation in the lock region. The following
* macros must be defined:
*
* FREE_LIST_HEAD -- the name of the head of the free list.
* STRUCT_NAME -- the name of the structure in the free list.
* CURRENT_COUNT -- structure element for count of current objects.
* MAX_COUNT -- structure element for max of current objects.
* STEAL_NAME -- name of stat to track steals.
* STEAL_EVENT -- name of event to track steals.
*/
#define __lock_alloc() /* for ctags */
{
struct STRUCT_NAME *sh_thing;
DB_LOCKPART *end_p, *cur_p, *orig_p;
DB_LOCKREGION *region;
int begin, locked;
u_int32_t i, nobjs;
region = lt->reginfo.primary;
orig_p = <->part_array[part_id];
if (region->part_t_size == 1)
goto alloc;
retry: MUTEX_UNLOCK(lt->env, orig_p->mtx_part);
locked = 0;
sh_thing = NULL;
end_p = <->part_array[region->part_t_size];
/*
* Start looking at the next partition and wrap around. If
* we get back to our partition then raise an error.
*/
begin = 0;
nobjs = 0;
cur_p = orig_p + 1;
again: for (; sh_thing == NULL && cur_p < end_p; cur_p++) {
MUTEX_LOCK(lt->env, cur_p->mtx_part);
if ((sh_thing = SH_TAILQ_FIRST(
&cur_p->FREE_LIST_HEAD, STRUCT_NAME)) != NULL)
SH_TAILQ_REMOVE(&cur_p->FREE_LIST_HEAD,
sh_thing, links, STRUCT_NAME);
MUTEX_UNLOCK(lt->env, cur_p->mtx_part);
}
if (sh_thing != NULL) {
MUTEX_LOCK(lt->env, orig_p->mtx_part);
SH_TAILQ_INSERT_HEAD(&orig_p->FREE_LIST_HEAD,
sh_thing, links, STRUCT_NAME);
STAT_INC_VERB(env,
lock, STEAL_EVENT, orig_p->part_stat.STEAL_NAME,
cur_p - lt->part_array, part_id);
return (0);
}
if (!begin) {
begin = 1;
cur_p = lt->part_array;
end_p = orig_p;
goto again;
}
/*
* Try to get some more space in the region.
*/
LOCK_REGION_LOCK(lt->env);
MUTEX_LOCK(lt->env, orig_p->mtx_part);
locked = 1;
nobjs = 0;
/* check to see if we raced with someone. */
if ((region->stat.MAX_COUNT == 0 ||
region->stat.CURRENT_COUNT < region->stat.MAX_COUNT) &&
SH_TAILQ_FIRST(&orig_p->FREE_LIST_HEAD, STRUCT_NAME) == NULL) {
MUTEX_UNLOCK(lt->env, orig_p->mtx_part);
alloc: locked = 0;
sh_thing = NULL;
cur_p = orig_p;
end_p = <->part_array[region->part_t_size];
nobjs = region->stat.CURRENT_COUNT >> 2;
/* Just in case. */
if (nobjs == 0)
nobjs = 1;
if (region->stat.MAX_COUNT != 0 &&
region->stat.MAX_COUNT <
region->stat.CURRENT_COUNT + nobjs)
nobjs = region->stat.MAX_COUNT -
region->stat.CURRENT_COUNT;
/*
* If the max memory is not sized for max objects,
* allocate as much as possible.
*/
F_SET(<->reginfo, REGION_TRACKED);
while (__env_alloc(<->reginfo,
nobjs * sizeof(struct STRUCT_NAME), &sh_thing) != 0)
if ((nobjs >>= 1) == 0)
break;
F_CLR(<->reginfo, REGION_TRACKED);
region->stat.CURRENT_COUNT += nobjs;
if (region->part_t_size != 1)
LOCK_REGION_UNLOCK(lt->env);
if (nobjs == 0)
goto err;
for (i = 0; i < nobjs; i++) {
memset(sh_thing, 0, sizeof (struct STRUCT_NAME));
if (&cur_p->free_locks ==
(struct __flock *)&cur_p->FREE_LIST_HEAD)
((struct __db_lock *)
sh_thing)->status = DB_LSTAT_FREE;
MUTEX_LOCK(lt->env, cur_p->mtx_part);
SH_TAILQ_INSERT_HEAD(&cur_p->FREE_LIST_HEAD,
sh_thing, links, STRUCT_NAME);
MUTEX_UNLOCK(lt->env, cur_p->mtx_part);
if (region->part_t_size != 1 && ++cur_p == end_p)
cur_p = lt->part_array;
sh_thing++;
}
if (region->part_t_size != 1)
MUTEX_LOCK(lt->env, orig_p->mtx_part);
locked = 1;
} else
LOCK_REGION_UNLOCK(lt->env);
if (SH_TAILQ_FIRST(&orig_p->FREE_LIST_HEAD, STRUCT_NAME) != NULL)
return (0);
/* Somone stole all the locks! */
if (nobjs > 0)
goto retry;
err: if (region->part_t_size != 1 && locked == 0)
MUTEX_LOCK(lt->env, orig_p->mtx_part);
return (__lock_nomem(lt->env, "lock entries"));
}