1
1

- Make attributes thread safe. Design decision: attributes are so far

away from the critical performance path that we do not attempt to
  provide fancy / fine-grained locking at all.  There is one global
  lock that protects most attribute actions.  Since attribute actions
  are non-blocking in nature, this will not cause deadlock.
- Fix a problem where attributes could get deleted from a communicator
  before the communicator was actually released.

This commit was SVN r3136.
Этот коммит содержится в:
Jeff Squyres 2004-10-14 18:06:40 +00:00
родитель 507448f86e
Коммит e23ab15da1
15 изменённых файлов: 135 добавлений и 61 удалений

Просмотреть файл

@ -1,12 +1,15 @@
/**
/*
* $HEADER$
*/
#include "ompi_config.h"
#include "attribute/attribute.h"
#include "communicator/communicator.h"
#include "threads/mutex.h"
#include "include/constants.h"
/**
/*
* Macros
*/
@ -61,7 +64,7 @@
/*
* Static
* Static
*/
static void ompi_attrkey_item_construct(ompi_attrkey_item_t *item);
static void ompi_attrkey_item_destruct(ompi_attrkey_item_t *item);
@ -84,6 +87,16 @@ OBJ_CLASS_INSTANCE(ompi_attrkey_item_t,
static ompi_hash_table_t *keyval_hash;
static ompi_bitmap_t *key_bitmap;
/*
* Have one lock protect all access to any attribute stuff (keyval
* hash, key bitmap, attribute hashes on MPI objects, etc.).
* Arguably, we would have a finer-grained scheme (e.g., 2 locks) that
* would allow at least *some* concurrency, but these are attributes
* -- they're not in the performance-critical portions of the code.
* So why bother?
*/
static ompi_mutex_t alock;
/*
* ompi_attrkey_item_t interface functions
@ -102,8 +115,10 @@ ompi_attrkey_item_destruct(ompi_attrkey_item_t *item)
{
/* Remove the key entry from the hash and free the key */
OMPI_THREAD_LOCK(&alock);
ompi_hash_table_remove_value_uint32(keyval_hash, item->key);
FREE_KEY(item->key);
OMPI_THREAD_UNLOCK(&alock);
}
@ -176,8 +191,10 @@ ompi_attr_create_keyval(ompi_attribute_type_t type,
/* Create a new unique key and fill the hash */
OMPI_THREAD_LOCK(&alock);
*key = CREATE_KEY();
ret = ompi_hash_table_set_value_uint32(keyval_hash, *key, attr);
OMPI_THREAD_UNLOCK(&alock);
if (OMPI_SUCCESS != ret) {
return ret;
}
@ -204,7 +221,7 @@ ompi_attr_create_keyval(ompi_attribute_type_t type,
int
ompi_attr_free_keyval(ompi_attribute_type_t type, int *key, int predefined)
ompi_attr_free_keyval(ompi_attribute_type_t type, int *key, bool predefined)
{
ompi_attrkey_item_t *key_item;
@ -216,15 +233,17 @@ ompi_attr_free_keyval(ompi_attribute_type_t type, int *key, int predefined)
/* Find the key-value pair */
OMPI_THREAD_LOCK(&alock);
key_item = (ompi_attrkey_item_t*)
ompi_hash_table_get_value_uint32(keyval_hash, *key);
OMPI_THREAD_UNLOCK(&alock);
if ((NULL == key_item) || (key_item->attr_type != type) ||
((!predefined) && (key_item->attr_flag & OMPI_KEYVAL_PREDEFINED))) {
return OMPI_ERR_BAD_PARAM;
}
/* Not releasing the object here, it will be done in MPI_*_attr_delete */
/* MPI says to set the returned value to MPI_KEYVAL_INVALID */
*key = MPI_KEYVAL_INVALID;
@ -241,7 +260,7 @@ ompi_attr_free_keyval(ompi_attribute_type_t type, int *key, int predefined)
int
ompi_attr_delete(ompi_attribute_type_t type, void *object,
ompi_hash_table_t *keyhash, int key,
int predefined)
bool predefined, bool need_lock)
{
ompi_attrkey_item_t *key_item;
int ret, err;
@ -253,19 +272,35 @@ ompi_attr_delete(ompi_attribute_type_t type, void *object,
return MPI_ERR_INTERN;
}
/* Note that this function can be invoked by
ompi_attr_delete_all() to set attributes on the new object (in
addition to the top-level MPI_* functions that set attributes).
In these cases, ompi_attr_delete_all() has already locked the
keyval_lock, so we should not try to lock it again. */
if (need_lock) {
OMPI_THREAD_LOCK(&alock);
}
/* Check if the key is valid in the key-attribute hash */
key_item = (ompi_attrkey_item_t*)
ompi_hash_table_get_value_uint32(keyval_hash, key);
if ((NULL == key_item) || (key_item->attr_type!= type) ||
((!predefined) && (key_item->attr_flag & OMPI_KEYVAL_PREDEFINED))) {
if (need_lock) {
OMPI_THREAD_UNLOCK(&alock);
}
return OMPI_ERR_BAD_PARAM;
}
/* Ensure that we don't have an empty keyhash */
if (NULL == keyhash) {
if (need_lock) {
OMPI_THREAD_UNLOCK(&alock);
}
return OMPI_ERR_BAD_PARAM;
}
@ -273,7 +308,6 @@ ompi_attr_delete(ompi_attribute_type_t type, void *object,
yes, then delete the attribute and key entry from the CWD hash */
attr = ompi_hash_table_get_value_uint32(keyhash, key);
switch(type) {
case COMM_ATTR:
DELETE_ATTR_OBJECT(communicator, attr);
@ -293,6 +327,9 @@ ompi_attr_delete(ompi_attribute_type_t type, void *object,
}
ret = ompi_hash_table_remove_value_uint32(keyhash, key);
if (need_lock) {
OMPI_THREAD_UNLOCK(&alock);
}
if (OMPI_SUCCESS != ret) {
return ret;
}
@ -309,7 +346,7 @@ ompi_attr_delete(ompi_attribute_type_t type, void *object,
int
ompi_attr_set(ompi_attribute_type_t type, void *object,
ompi_hash_table_t **keyhash, int key, void *attribute,
int predefined)
bool predefined, bool need_lock)
{
ompi_attrkey_item_t *key_item;
int ret, err;
@ -325,6 +362,15 @@ ompi_attr_set(ompi_attribute_type_t type, void *object,
return MPI_ERR_INTERN;
}
/* Note that this function can be invoked by ompi_attr_copy_all()
to set attributes on the new object (in addition to the
top-level MPI_* functions that set attributes). In these
cases, ompi_attr_copy_all() has already locked the keyval_lock,
so we should not try to lock it again. */
if (need_lock) {
OMPI_THREAD_LOCK(&alock);
}
key_item = (ompi_attrkey_item_t *)
ompi_hash_table_get_value_uint32(keyval_hash, key);
@ -332,6 +378,9 @@ ompi_attr_set(ompi_attribute_type_t type, void *object,
if ((NULL == key_item) || (key_item->attr_type != type) ||
((!predefined) && (key_item->attr_flag & OMPI_KEYVAL_PREDEFINED))) {
if (need_lock) {
OMPI_THREAD_UNLOCK(&alock);
}
return OMPI_ERR_BAD_PARAM;
}
@ -343,8 +392,8 @@ ompi_attr_set(ompi_attribute_type_t type, void *object,
/* Now see if the key is present in the CWD object. If so, delete
the old attribute in the key */
oldattr = ompi_hash_table_get_value_uint32(*keyhash, key);
oldattr = ompi_hash_table_get_value_uint32(*keyhash, key);
if (oldattr != NULL) {
switch(type) {
case COMM_ATTR:
@ -367,6 +416,9 @@ ompi_attr_set(ompi_attribute_type_t type, void *object,
}
ret = ompi_hash_table_set_value_uint32(*keyhash, key, attribute);
if (need_lock) {
OMPI_THREAD_UNLOCK(&alock);
}
if (OMPI_SUCCESS != ret) {
return ret;
}
@ -394,10 +446,12 @@ ompi_attr_get(ompi_hash_table_t *keyhash, int key, void *attribute,
flag argument */
*flag = 0;
OMPI_THREAD_LOCK(&alock);
key_item = (ompi_attrkey_item_t *)
ompi_hash_table_get_value_uint32(keyval_hash, key);
if (NULL == key_item) {
OMPI_THREAD_UNLOCK(&alock);
return MPI_KEYVAL_INVALID;
}
@ -405,10 +459,12 @@ ompi_attr_get(ompi_hash_table_t *keyhash, int key, void *attribute,
been cached on this object yet. So just return *flag = 0. */
if (NULL == keyhash) {
OMPI_THREAD_UNLOCK(&alock);
return MPI_SUCCESS;
}
attr = ompi_hash_table_get_value_uint32(keyhash, key);
OMPI_THREAD_UNLOCK(&alock);
if (NULL != attr) {
*((void **) attribute) = attr;
*flag = 1;
@ -443,6 +499,12 @@ ompi_attr_copy_all(ompi_attribute_type_t type, void *old_object,
return MPI_SUCCESS;
}
/* Lock this whole sequence of events -- don't let any other
thread modify the structure of the keyval hash or bitmap while
we're traversing it */
OMPI_THREAD_LOCK(&alock);
/* Get the first key-attr in the CWD hash */
ret = ompi_hash_table_get_first_key_uint32(oldkeyhash, &key, &old_attr,
&node);
@ -478,23 +540,27 @@ ompi_attr_copy_all(ompi_attribute_type_t type, void *old_object,
/* Hang this off the new CWD object */
/* VPS: predefined is set to 1, so that no comparison is done
for prdefined at all and it just falls off the error
checking loop in attr_set */
/* The "predefined" parameter to ompi_attr_set() is set to 1,
so that no comparison is done for prdefined at all and it
just falls off the error checking loop in attr_set */
/* VPS: we pass the address of new_attr in here, I am assuming
that new_attr should have actually been a double pointer in
the copy fn, but since its a pointer in that MPI specs, we
need to pass *new_attr here */
if (flag == 1) {
if (1 == flag) {
ompi_attr_set(type, new_object, &newkeyhash, key,
new_attr, 1);
new_attr, true, false);
}
ret = ompi_hash_table_get_next_key_uint32(oldkeyhash, &key,
&old_attr, in_node,
&node);
}
/* All done */
OMPI_THREAD_UNLOCK(&alock);
return MPI_SUCCESS;
}
@ -515,10 +581,16 @@ ompi_attr_delete_all(ompi_attribute_type_t type, void *object,
/* Ensure that the table is not empty */
if (NULL != keyhash) {
if (NULL == keyhash) {
return MPI_SUCCESS;
}
/* Lock this whole sequence of events -- don't let any other
thread modify the structure of the keyval hash or bitmap while
we're traversing it */
OMPI_THREAD_LOCK(&alock);
/* Get the first key in local CWD hash */
ret = ompi_hash_table_get_first_key_uint32(keyhash,
&key, &old_attr,
@ -538,8 +610,11 @@ ompi_attr_delete_all(ompi_attribute_type_t type, void *object,
in_node, &node);
/* Now delete this attribute */
ompi_attr_delete(type, object, keyhash, oldkey, 1);
ompi_attr_delete(type, object, keyhash, oldkey, true, false);
}
/* All done */
OMPI_THREAD_UNLOCK(&alock);
return MPI_SUCCESS;
}

Просмотреть файл

@ -171,7 +171,8 @@ int ompi_attr_create_keyval(ompi_attribute_type_t type,
* @return OMPI error code
*/
int ompi_attr_free_keyval(ompi_attribute_type_t type, int *key, int predefined);
int ompi_attr_free_keyval(ompi_attribute_type_t type, int *key,
bool predefined);
/**
* Set an attribute on the comm/win/datatype
@ -181,16 +182,22 @@ int ompi_attr_free_keyval(ompi_attribute_type_t type, int *key, int predefined);
* @param key Key val for the attribute (IN)
* @param attribute The actual attribute pointer (IN)
* @param predefined Whether the key is predefined or not 0/1 (IN)
* @param need_lock Whether we need to need to lock the keyval_lock or not
* @return OMPI error code
*
* If (*keyhash) == NULL, a new keyhash will be created and
* initialized.
*
* Note that need_lock should *always* be true when this function is
* invoked from an top-level MPI function. It is only false when this
* function is invoked internally (i.e., when we already hold the
* relevant locks, and we don't want to try to lock them again,
* recursively).
*/
int ompi_attr_set(ompi_attribute_type_t type, void *object,
ompi_hash_table_t **keyhash,
int key, void *attribute, int predefined);
int key, void *attribute, bool predefined, bool need_lock);
/**
* Get an attribute on the comm/win/datatype
@ -214,13 +221,19 @@ int ompi_attr_get(ompi_hash_table_t *keyhash, int key,
* @param keyhash The attribute hash table hanging on the object(IN)
* @param key Key val for the attribute (IN)
* @param predefined Whether the key is predefined or not 0/1 (IN)
* @param need_lock Whether we need to need to lock the keyval_lock or not
* @return OMPI error code
*
* Note that need_lock should *always* be true when this function is
* invoked from an top-level MPI function. It is only false when this
* function is invoked internally (i.e., when we already hold the
* relevant locks, and we don't want to try to lock them again,
* recursively).
*/
int ompi_attr_delete(ompi_attribute_type_t type, void *object,
ompi_hash_table_t *keyhash , int key,
int predefined);
ompi_hash_table_t *keyhash , int key,
bool predefined, bool need_lock);
/**

Просмотреть файл

@ -167,7 +167,7 @@ static int set(int target_keyval, void *value)
return err;
}
err = ompi_attr_set(COMM_ATTR, MPI_COMM_WORLD,
&MPI_COMM_WORLD->c_keyhash, keyval, value, 1);
&MPI_COMM_WORLD->c_keyhash, keyval, value, true, true);
if (OMPI_SUCCESS != err) {
return err;
}

Просмотреть файл

@ -678,13 +678,6 @@ static int ompi_comm_allgather_emulate_intra( void *inbuf, int incount,
*/
int ompi_comm_free ( ompi_communicator_t **comm )
{
/* Release attributes */
if (NULL != (*comm)->c_keyhash) {
ompi_attr_delete_all ( COMM_ATTR, (*comm), (*comm)->c_keyhash );
OBJ_RELEASE((*comm)->c_keyhash);
}
/* Release the communicator */
OBJ_RELEASE ( (*comm) );

Просмотреть файл

@ -276,6 +276,13 @@ static void ompi_comm_construct(ompi_communicator_t* comm)
static void ompi_comm_destruct(ompi_communicator_t* comm)
{
/* Release attributes */
if (NULL != comm->c_keyhash) {
ompi_attr_delete_all(COMM_ATTR, comm, comm->c_keyhash);
OBJ_RELEASE(comm->c_keyhash);
}
/* Release the collective module */
mca_coll_base_comm_unselect(comm);

Просмотреть файл

@ -32,7 +32,8 @@ int MPI_Attr_delete(MPI_Comm comm, int keyval)
}
}
ret = ompi_attr_delete(COMM_ATTR, comm, comm->c_keyhash, keyval, 0);
ret = ompi_attr_delete(COMM_ATTR, comm, comm->c_keyhash, keyval,
false, true);
OMPI_ERRHANDLER_RETURN(ret, comm, MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -32,7 +32,7 @@ int MPI_Attr_put(MPI_Comm comm, int keyval, void *attribute_val)
}
ret = ompi_attr_set(COMM_ATTR, comm, &comm->c_keyhash,
keyval, attribute_val, 0);
keyval, attribute_val, false, true);
OMPI_ERRHANDLER_RETURN(ret, comm, MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -31,7 +31,8 @@ int MPI_Comm_delete_attr(MPI_Comm comm, int comm_keyval)
}
}
ret = ompi_attr_delete(COMM_ATTR, comm, comm->c_keyhash, comm_keyval, 0);
ret = ompi_attr_delete(COMM_ATTR, comm, comm->c_keyhash, comm_keyval,
false, true);
OMPI_ERRHANDLER_RETURN(ret, comm, MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -32,6 +32,6 @@ int MPI_Comm_set_attr(MPI_Comm comm, int comm_keyval, void *attribute_val)
}
ret = ompi_attr_set(COMM_ATTR, comm, &comm->c_keyhash,
comm_keyval, attribute_val, 0);
comm_keyval, attribute_val, false, true);
OMPI_ERRHANDLER_RETURN(ret, comm, MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -32,7 +32,8 @@ int MPI_Type_delete_attr (MPI_Datatype type, int type_keyval)
}
}
ret = ompi_attr_delete(TYPE_ATTR, type, type->d_keyhash, type_keyval, 0);
ret = ompi_attr_delete(TYPE_ATTR, type, type->d_keyhash, type_keyval,
false, true);
OMPI_ERRHANDLER_RETURN(ret, MPI_COMM_WORLD,
MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -35,7 +35,7 @@ int MPI_Type_set_attr (MPI_Datatype type,
}
ret = ompi_attr_set(TYPE_ATTR, type, &type->d_keyhash,
type_keyval, attribute_val, 0);
type_keyval, attribute_val, false, true);
OMPI_ERRHANDLER_RETURN(ret, MPI_COMM_WORLD,
MPI_ERR_OTHER, FUNC_NAME);

Просмотреть файл

@ -31,6 +31,7 @@ int MPI_Win_delete_attr(MPI_Win win, int win_keyval)
}
}
ret = ompi_attr_delete(WIN_ATTR, win, win->w_keyhash, win_keyval, 0);
ret = ompi_attr_delete(WIN_ATTR, win, win->w_keyhash, win_keyval,
false, true);
OMPI_ERRHANDLER_RETURN(ret, win, MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -34,6 +34,6 @@ int MPI_Win_set_attr(MPI_Win win, int win_keyval, void *attribute_val)
}
ret = ompi_attr_set(WIN_ATTR, win, &win->w_keyhash,
win_keyval, attribute_val, 0);
win_keyval, attribute_val, false, true);
OMPI_ERRHANDLER_RETURN(ret, win, MPI_ERR_OTHER, FUNC_NAME);
}

Просмотреть файл

@ -54,8 +54,4 @@ void mpi_type_delete_attr_f(MPI_Fint *type, MPI_Fint *type_keyval,
*ierr = OMPI_INT_2_FINT(MPI_Type_delete_attr(c_type,
OMPI_FINT_2_INT(*type_keyval)
));
if (MPI_SUCCESS == *ierr) {
*type = MPI_Type_c2f( c_type );
}
}

Просмотреть файл

@ -46,27 +46,13 @@ OMPI_GENERATE_F77_BINDINGS (MPI_WIN_DELETE_ATTR,
#if OMPI_PROFILE_LAYER && ! OMPI_HAVE_WEAK_SYMBOLS
#include "mpi/f77/profile/defines.h"
#endif
static const char FUNC_NAME[] = "MPI_Win_delete_attr_f";
void mpi_win_delete_attr_f(MPI_Fint *win, MPI_Fint *win_keyval, MPI_Fint *ierr)
{
MPI_Win c_win = MPI_Win_f2c( *win );
int ret, c_err;
MPI_Win c_win = MPI_Win_f2c(*win);
if (MPI_PARAM_CHECK) {
if (MPI_WIN_NULL == c_win) {
c_err = OMPI_ERRHANDLER_INVOKE(c_win, MPI_ERR_WIN,
FUNC_NAME);
*ierr = OMPI_INT_2_FINT(c_err);
}
}
ret = ompi_attr_delete(WIN_ATTR, c_win, c_win->w_keyhash, *win_keyval,
OMPI_KEYVAL_F77);
if (MPI_SUCCESS != ret) {
OMPI_ERRHANDLER_INVOKE(MPI_COMM_WORLD, MPI_ERR_OTHER, FUNC_NAME);
} else {
*ierr = MPI_SUCCESS;
}
*ierr = OMPI_INT_2_FINT(MPI_Win_delete_attr(c_win,
OMPI_FINT_2_INT(*win_keyval)
));
}