Hello,
this gives the option to use the umem cache feature from the libumem
[1]
for the opal object system.
It is full backward compatible to the old system.
But the patch exists of more changes:
(1) reorder opal_class_t, in the hope that vital members fit in the
first
cache line
(2) a per class lock for initialization
(3) the global class list is now a linked list embeded in the
opal_class_t
(this can be reduced to a stack/single linked list)
(4) new contructors/destructors for the one time cache initialization
To complile with this new feature you must configure open-mpi with
"-DUSE_UMEM" in your CFLAGS, and all other needed build flags to
find the
header and library of lubumem (LDFLAGS, LIBS).
To full use the object caching of libumem you can use the new macro
OBJ_CLASS_INSTANCE_CACHE() which have arguments for the cache
contructors/destructors.
In the followup mail, I convert the opal_free_list_t and
orte_pointer_array_t to use the cache for the initialization of the
opal_mutex_t and opal_condition_t members.
I have just compiled it with and without USE_UMEM but no benchmarking.
Comments welcome.
Greetings
Bert Wesarg
PS: I know that you are busy with the OMPI 1.2 release, I just want to
send it out, befor I forget it.
[1] solaris: built-in
other: http://sourceforge.net/projects/umem
---
opal/class/opal_object.c | 210 ++++++++++++++++++++++++++++++
+----------------
opal/class/opal_object.h | 201 +++++++++++++++++++++++++++++++++++
+++++----
2 files changed, 324 insertions(+), 87 deletions(-)
diff --quilt old/opal/class/opal_object.h new/opal/class/opal_object.h
--- old/opal/class/opal_object.h
+++ new/opal/class/opal_object.h
@@ -122,10 +122,17 @@
#if OMPI_HAVE_THREAD_SUPPORT
#include "opal/sys/atomic.h"
#endif /* OMPI_HAVE_THREAD_SUPPORT */
+#ifdef USE_UMEM
+# include <umem.h>
+# ifndef UMEM_CACHE_NAMELEN
+# define UMEM_CACHE_NAMELEN 31
+# endif
+#endif
+
#if OMPI_ENABLE_DEBUG
/* Any kind of unique ID should do the job */
#define OPAL_OBJ_MAGIC_ID ((0xdeafbeedULL << 32) + 0xdeafbeedULL)
#endif
@@ -144,21 +151,36 @@ typedef void (*opal_destruct_t) (opal_ob
*
* There should be a single instance of this descriptor for each
class
* definition.
*/
struct opal_class_t {
- const char *cls_name; /**< symbolic name for class */
- opal_class_t *cls_parent; /**< parent class descriptor */
- opal_construct_t cls_construct; /**< class constructor */
- opal_destruct_t cls_destruct; /**< class destructor */
- int cls_initialized; /**< is class initialized */
- int cls_depth; /**< depth of class hierarchy
tree */
+#ifdef USE_UMEM
+ umem_cache_t *cls_cache; /**< object cache */
+#endif
+ size_t cls_sizeof; /**< size of an object
instance */
+ opal_construct_t *cls_cache_construct_array;
+ /**< array of parent class cache
constructors */
opal_construct_t *cls_construct_array;
/**< array of parent class
constructors */
- opal_destruct_t *cls_destruct_array;
+ opal_destruct_t *cls_destruct_array;
/**< array of parent class
destructors */
- size_t cls_sizeof; /**< size of an object
instance */
+ opal_destruct_t *cls_cache_destruct_array;
+ /**< array of parent class cache
destructors */
+ int cls_initialized; /**< is class initialized */
+ int cls_depth; /**< depth of class hierarchy
tree */
+ const char *cls_name; /**< symbolic name for class */
+ opal_class_t *cls_parent; /**< parent class descriptor */
+ opal_construct_t cls_construct; /**< class constructor */
+ opal_destruct_t cls_destruct; /**< class destructor */
+ opal_construct_t cls_cache_construct;
+ /**< class object cache
constructor */
+ opal_destruct_t cls_cache_destruct;
+ /**< class cache destructor */
+ opal_atomic_lock_t cls_init_lock;
+ /**< class init mutex */
+ opal_class_t *cls_next, *cls_prev;
+ /**< linked list of all
classes */
};
/**
* For static initializations of OBJects.
*
@@ -198,30 +220,97 @@ struct opal_object_t {
* @param NAME Name of class
* @return Pointer to class descriptor
*/
#define OBJ_CLASS(NAME) (&(NAME ## _class))
-
/**
* Static initializer for a class descriptor
*
* @param NAME Name of class
* @param PARENT Name of parent class
* @param CONSTRUCTOR Pointer to constructor
* @param DESTRUCTOR Pointer to destructor
*
* Put this in NAME.c
*/
+#ifdef USE_UMEM
#define OBJ_CLASS_INSTANCE(NAME, PARENT, CONSTRUCTOR,
DESTRUCTOR) \
opal_class_t NAME ## _class =
{ \
+
NULL, \
+ sizeof
(NAME), \
+ NULL, NULL, NULL,
NULL, \
+ 0,
0, \
+ #
NAME, \
+ OBJ_CLASS
(PARENT), \
+ (opal_construct_t)
CONSTRUCTOR, \
+ (opal_destruct_t)
DESTRUCTOR, \
+ (opal_construct_t)
NULL, \
+ (opal_destruct_t)
NULL, \
+
{ { OPAL_ATOMIC_UNLOCKED } }, \
+ OBJ_CLASS(NAME), OBJ_CLASS
(NAME) \
+ }
+#else
+#define OBJ_CLASS_INSTANCE(NAME, PARENT, CONSTRUCTOR,
DESTRUCTOR) \
+ opal_class_t NAME ## _class =
{ \
+ sizeof
(NAME), \
+ NULL, NULL, NULL,
NULL, \
+ 0,
0, \
+ #
NAME, \
+ OBJ_CLASS
(PARENT), \
+ (opal_construct_t)
CONSTRUCTOR, \
+ (opal_destruct_t)
DESTRUCTOR, \
+ (opal_construct_t)
NULL, \
+ (opal_destruct_t)
NULL, \
+
{ { OPAL_ATOMIC_UNLOCKED } }, \
+ OBJ_CLASS(NAME), OBJ_CLASS
(NAME) \
+ }
+#endif
+
+/**
+ * Static initializer for a class descriptor with cache ctor/dtor
+ *
+ * @param NAME Name of class
+ * @param PARENT Name of parent class
+ * @param CONSTRUCTOR Pointer to constructor
+ * @param DESTRUCTOR Pointer to destructor
+ * @param CACHE_CONSTRUCTOR Pointer to cache constructor
+ * @param CACHE_DESTRUCTOR Pointer to cache destructor
+ *
+ * Put this in NAME.c
+ */
+#ifdef USE_UMEM
+#define OBJ_CLASS_INSTANCE_CACHE(NAME, PARENT, CONSTRUCTOR,
DESTRUCTOR, CACHE_CONSTRUCTOR, CACHE_DESTRUCTOR) \
+ opal_class_t NAME ## _class =
{ \
+
NULL, \
+ sizeof
(NAME), \
+ NULL, NULL, NULL,
NULL, \
+ 0,
0, \
#
NAME, \
OBJ_CLASS
(PARENT), \
(opal_construct_t)
CONSTRUCTOR, \
(opal_destruct_t)
DESTRUCTOR, \
- 0, 0, NULL,
NULL, \
- sizeof
(NAME) \
+ (opal_construct_t)
CACHE_CONSTRUCTOR, \
+ (opal_destruct_t)
CACHE_DESTRUCTOR, \
+
{ { OPAL_ATOMIC_UNLOCKED } }, \
+ OBJ_CLASS(NAME), OBJ_CLASS
(NAME) \
}
+#else
+#define OBJ_CLASS_INSTANCE_CACHE(NAME, PARENT, CONSTRUCTOR,
DESTRUCTOR, CACHE_CONSTRUCTOR, CACHE_DESTRUCTOR) \
+ opal_class_t NAME ## _class =
{ \
+ sizeof
(NAME), \
+ NULL, NULL, NULL,
NULL, \
+ 0,
0, \
+ #
NAME, \
+ OBJ_CLASS
(PARENT), \
+ (opal_construct_t)
CONSTRUCTOR, \
+ (opal_destruct_t)
DESTRUCTOR, \
+ (opal_construct_t)
CACHE_CONSTRUCTOR, \
+ (opal_destruct_t)
CACHE_DESTRUCTOR, \
+
{ { OPAL_ATOMIC_UNLOCKED } }, \
+ OBJ_CLASS(NAME), OBJ_CLASS
(NAME) \
+ }
+#endif
/**
* Declaration for class descriptor
*
@@ -308,28 +397,25 @@ static inline opal_object_t *opal_obj_ne
do
{ \
assert(NULL != ((opal_object_t *) (object))-
>obj_class); \
assert(OPAL_OBJ_MAGIC_ID == ((opal_object_t *) (object))-
>obj_magic_id); \
if (0 == opal_obj_update((opal_object_t *) (object), -1))
{ \
OBJ_SET_MAGIC_ID((object),
0); \
- opal_obj_run_destructors((opal_object_t *)
(object)); \
- OBJ_REMEMBER_FILE_AND_LINENO( object, __FILE__,
__LINE__ ); \
- free
(object); \
+ opal_object_release_internal((opal_object_t *)
(object), \
+ __FILE__,
__LINE__); \
object =
NULL; \
}
\
} while (0)
#else
#define OBJ_RELEASE
(object) \
do
{ \
if (0 == opal_obj_update((opal_object_t *) (object), -1))
{ \
- opal_obj_run_destructors((opal_object_t *)
(object)); \
- free
(object); \
+ opal_object_release_internal((opal_object_t *)
(object)); \
object =
NULL; \
}
\
} while (0)
#endif
-
/**
* Construct (initialize) objects that are not dynamically allocated.
*
* @param object Pointer to the object
* @param type The object type
@@ -346,10 +432,11 @@ do {
if (0 == (type)->cls_initialized) { \
opal_class_initialize((type)); \
} \
((opal_object_t *) (object))->obj_class = (type); \
((opal_object_t *) (object))->obj_reference_count = 1; \
+ opal_obj_run_cache_constructors((opal_object_t *) (object)); \
opal_obj_run_constructors((opal_object_t *) (object)); \
OBJ_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
} while (0)
@@ -361,16 +448,18 @@ do {
#if OMPI_ENABLE_DEBUG
#define OBJ_DESTRUCT(object) \
do { \
assert(OPAL_OBJ_MAGIC_ID == ((opal_object_t *) (object))-
>obj_magic_id); \
OBJ_SET_MAGIC_ID((object), 0); \
+ opal_obj_run_cache_destructors((opal_object_t *) (object)); \
opal_obj_run_destructors((opal_object_t *) (object)); \
OBJ_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
} while (0)
#else
#define OBJ_DESTRUCT(object) \
do { \
+ opal_obj_run_cache_destructors((opal_object_t *) (object)); \
opal_obj_run_destructors((opal_object_t *) (object)); \
OBJ_REMEMBER_FILE_AND_LINENO( object, __FILE__, __LINE__ ); \
} while (0)
#endif
@@ -400,10 +489,11 @@ OPAL_DECLSPEC void opal_class_initialize
* upon process termination.
*/
OPAL_DECLSPEC int opal_class_finalize(void);
END_C_DECLS
+
/**
* Run the hierarchy of class constructors for this object, in a
* parent-first order.
*
* Do not use this function directly: use OBJ_CONSTRUCT() instead.
@@ -449,10 +539,58 @@ static inline void opal_obj_run_destruct
}
}
/**
+ * Run the hierarchy of class cache constructors for this object,
in a
+ * parent-first order.
+ *
+ * Do not use this function directly: use OBJ_CONSTRUCT() instead.
+ *
+ * WARNING: This implementation relies on a hardwired maximum
depth of
+ * the inheritance tree!!!
+ *
+ * Hardwired for fairly shallow inheritance trees
+ * @param size Pointer to the object.
+ */
+static inline void opal_obj_run_cache_constructors(opal_object_t *
object)
+{
+ opal_construct_t* cls_cache_construct;
+
+ assert(NULL != object->obj_class);
+
+ cls_cache_construct = object->obj_class-
>cls_cache_construct_array;
+ while( NULL != *cls_cache_construct ) {
+ (*cls_cache_construct)(object);
+ cls_cache_construct++;
+ }
+}
+
+
+/**
+ * Run the hierarchy of class destructors for this object, in a
+ * parent-last order.
+ *
+ * Do not use this function directly: use OBJ_DESTRUCT() instead.
+ *
+ * @param size Pointer to the object.
+ */
+static inline void opal_obj_run_cache_destructors(opal_object_t *
object)
+{
+ opal_destruct_t* cls_cache_destruct;
+
+ assert(NULL != object->obj_class);
+
+ cls_cache_destruct = object->obj_class->cls_cache_destruct_array;
+ while( NULL != *cls_cache_destruct ) {
+ (*cls_cache_destruct)(object);
+ cls_cache_destruct++;
+ }
+}
+
+
+/**
* Create new object: dynamically allocate storage and run the class
* constructor.
*
* Do not use this function directly: use OBJ_NEW() instead.
*
@@ -461,25 +599,50 @@ static inline void opal_obj_run_destruct
* @return Pointer to the object
*/
static inline opal_object_t *opal_obj_new(opal_class_t * cls)
{
opal_object_t *object;
- assert(cls->cls_sizeof >= sizeof(opal_object_t));
- object = (opal_object_t *) malloc(cls->cls_sizeof);
if (0 == cls->cls_initialized) {
opal_class_initialize(cls);
}
+
+#ifdef USE_UMEM
+ object = (opal_object_t *) umem_cache_alloc(cls->cls_cache,
UMEM_NOFAIL);
+#else
+ object = (opal_object_t *) malloc(cls->cls_sizeof);
+#endif
+
if (NULL != object) {
+#ifndef USE_UMEM
object->obj_class = cls;
object->obj_reference_count = 1;
+ opal_obj_run_cache_constructors(object);
+#endif
opal_obj_run_constructors(object);
}
return object;
}
+static inline void opal_object_release_internal(opal_object_t *object
+#if OMPI_ENABLE_DEBUG
+ , const char *file, int line
+#endif
+ )
+{
+ opal_obj_run_destructors(object);
+ OBJ_REMEMBER_FILE_AND_LINENO(object, file, line);
+
+#ifdef USE_UMEM
+ umem_cache_free(object->obj_class->cls_cache, object);
+#else
+ opal_obj_run_cache_destructors(object);
+ free(object);
+#endif
+}
+
/**
* Atomically update the object's reference count by some increment.
*
* This function should not be used directly: it is called via the
* macros OBJ_RETAIN and OBJ_RELEASE
diff --quilt old/opal/class/opal_object.c new/opal/class/opal_object.c
--- old/opal/class/opal_object.c
+++ new/opal/class/opal_object.c
@@ -34,178 +34,252 @@
* Instantiation of class descriptor for the base class. This is
* special, since be mark it as already initialized, with no parent
* and no constructor or destructor.
*/
opal_class_t opal_object_t_class = {
+#ifdef USE_UMEM
+ NULL, /* object cache */
+#endif
+ sizeof(opal_object_t),/* size of the opal object */
+ NULL, /* array of cache constructors */
+ NULL, /* array of constructors */
+ NULL, /* array of destructors */
+ NULL, /* array of cache destructors */
+ 1, /* initialized -- this class is
preinitialized */
+ 0, /* class hierarchy depth */
"opal_object_t", /* name */
NULL, /* parent class */
NULL, /* constructor */
NULL, /* destructor */
- 1, /* initialized -- this class is
preinitialized */
- 0, /* class hierarchy depth */
- NULL, /* array of constructors */
- NULL, /* array of destructors */
- sizeof(opal_object_t) /* size of the opal object */
+ NULL, /* cache constructor */
+ NULL, /* cache destructor */
+ { { OPAL_ATOMIC_UNLOCKED } }, /* init lock */
+ &opal_object_t_class,
+ &opal_object_t_class
};
/*
* Local variables
*/
static opal_atomic_lock_t class_lock = { { OPAL_ATOMIC_UNLOCKED } };
-static void** classes = NULL;
-static int num_classes = 0;
-static int max_classes = 0;
-static const int increment = 10;
/*
* Local functions
*/
static void save_class(opal_class_t *cls);
-static void expand_array(void);
+#ifdef USE_UMEM
+
+static int
+run_cache_constructors_umem(void *buf, void *private, int flags)
+{
+ opal_object_t *object = (opal_object_t *)buf;
+ opal_class_t *cls = (opal_class_t *)private;
+
+ object->obj_class = cls;
+ object->obj_reference_count = 1;
+
+ opal_obj_run_cache_constructors(object);
+ return 0;
+}
+
+static void
+run_cache_destructors_umem(void *buf, void *private)
+{
+ opal_object_t *object = (opal_object_t *)buf;
+ opal_class_t *cls = (opal_class_t *)private;
+
+ assert(object->obj_class == cls);
+ assert(object->obj_reference_count == 0);
+
+ opal_obj_run_cache_destructors(object);
+
+ return;
+}
+
+#endif
+
/*
* Lazy initialization of class descriptor.
*/
void opal_class_initialize(opal_class_t *cls)
{
opal_class_t *c;
- opal_construct_t* cls_construct_array;
- opal_destruct_t* cls_destruct_array;
+ opal_construct_t *cls_construct_array;
+ opal_destruct_t *cls_destruct_array;
+ opal_construct_t *cls_cache_construct_array;
+ opal_destruct_t *cls_cache_destruct_array;
int cls_construct_array_count;
int cls_destruct_array_count;
+ int cls_cache_construct_array_count;
+ int cls_cache_destruct_array_count;
int i;
assert(cls);
+ assert(cls->cls_sizeof >= sizeof(opal_object_t));
/* Check to see if any other thread got in here and initialized
this class before we got a chance to */
if (1 == cls->cls_initialized) {
return;
}
- opal_atomic_lock(&class_lock);
+ opal_atomic_lock(&cls->cls_init_lock);
/* If another thread initializing this same class came in at
roughly the same time, it may have gotten the lock and
initialized. So check again. */
if (1 == cls->cls_initialized) {
- opal_atomic_unlock(&class_lock);
+ opal_atomic_unlock(&cls->cls_init_lock);
return;
}
/*
* First calculate depth of class hierarchy
* And the number of constructors and destructors
+ * And the number of cache constructors and cache destructors
*/
cls->cls_depth = 0;
- cls_construct_array_count = 0;
- cls_destruct_array_count = 0;
+ cls_construct_array_count = 0;
+ cls_destruct_array_count = 0;
+ cls_cache_construct_array_count = 0;
+ cls_cache_destruct_array_count = 0;
for (c = cls; c; c = c->cls_parent) {
- if( NULL != c->cls_construct ) {
- cls_construct_array_count++;
- }
- if( NULL != c->cls_destruct ) {
- cls_destruct_array_count++;
- }
+ cls_construct_array_count += !!(NULL != c-
>cls_construct);
+ cls_destruct_array_count += !!(NULL != c-
>cls_destruct);
+ cls_cache_construct_array_count += !!(NULL != c-
>cls_cache_construct);
+ cls_cache_destruct_array_count += !!(NULL != c-
>cls_cache_destruct);
cls->cls_depth++;
}
/*
* Allocate arrays for hierarchy of constructors and destructors
* plus for each a NULL-sentinel
*/
- cls->cls_construct_array =
- (void (**)(opal_object_t*))malloc
((cls_construct_array_count +
-
cls_destruct_array_count + 2) *
- sizeof(opal_construct_t) );
- if (NULL == cls->cls_construct_array) {
+ cls->cls_cache_construct_array =
+ (void (**)(opal_object_t*))malloc
((cls_cache_construct_array_count +
+
cls_construct_array_count +
+ cls_destruct_array_count +
+
cls_cache_destruct_array_count + 4
+ ) * sizeof
(opal_construct_t));
+ if (NULL == cls->cls_cache_construct_array) {
perror("Out of memory");
exit(-1);
}
+ cls->cls_construct_array =
+ cls->cls_cache_construct_array +
cls_cache_construct_array_count + 1;
cls->cls_destruct_array =
- cls->cls_construct_array + cls_construct_array_count + 1;
+ cls->cls_cache_construct_array +
cls_cache_construct_array_count +
+ cls_construct_array_count
+ 2;
+ cls->cls_cache_destruct_array =
+ cls->cls_cache_construct_array +
cls_cache_construct_array_count +
+ cls_construct_array_count +
+ cls_destruct_array_count
+ 3;
/*
- * The constructor array is reversed, so start at the end
+ * The (cache) constructor array is reversed, so start at the end
*/
- cls_construct_array = cls->cls_construct_array +
cls_construct_array_count;
- cls_destruct_array = cls->cls_destruct_array;
+ cls_cache_construct_array =
+ cls->cls_cache_construct_array +
cls_cache_construct_array_count;
+ cls_construct_array =
+ cls->cls_construct_array + cls_construct_array_count;
+ cls_destruct_array = cls->cls_destruct_array;
+ cls_cache_destruct_array = cls->cls_cache_destruct_array;
c = cls;
- *cls_construct_array = NULL; /* end marker for the
constructors */
+ *cls_cache_construct_array = NULL; /* end marker for the
constructors */
+ *cls_construct_array = NULL; /* end marker for the
constructors */
for (i = 0; i < cls->cls_depth; i++) {
if( NULL != c->cls_construct ) {
--cls_construct_array;
*cls_construct_array = c->cls_construct;
}
if( NULL != c->cls_destruct ) {
*cls_destruct_array = c->cls_destruct;
cls_destruct_array++;
}
+ if( NULL != c->cls_cache_construct ) {
+ --cls_cache_construct_array;
+ *cls_cache_construct_array = c->cls_cache_construct;
+ }
+ if( NULL != c->cls_cache_destruct ) {
+ *cls_cache_destruct_array = c->cls_cache_destruct;
+ cls_cache_destruct_array++;
+ }
c = c->cls_parent;
}
- *cls_destruct_array = NULL; /* end marker for the destructors */
+ *cls_destruct_array = NULL; /* end marker for the
destructors */
+ *cls_cache_destruct_array = NULL; /* end marker for the
destructors */
+
+#ifdef USE_UMEM
+ cls->cls_cache = umem_cache_create(
+ (char *)cls->cls_name, cls->cls_sizeof, 0,
+ run_cache_constructors_umem,
+ run_cache_destructors_umem,
+ NULL, cls, NULL, 0
+ );
+ if (NULL == cls->cls_cache) {
+ perror("Out of memory");
+ exit(-1);
+ }
+#endif
cls->cls_initialized = 1;
+ opal_atomic_lock(&cls->cls_init_lock);
+
save_class(cls);
/* All done */
-
- opal_atomic_unlock(&class_lock);
}
/*
* Note that this is finalize for *all* classes.
*/
int opal_class_finalize(void)
{
- int i;
+ opal_class_t *cls, *cnext;
- if (NULL != classes) {
- for (i = 0; i < num_classes; ++i) {
- if (NULL != classes[i]) {
- free(classes[i]);
- }
- }
- free(classes);
- classes = NULL;
- num_classes = 0;
- max_classes = 0;
+ opal_atomic_lock(&class_lock);
+ for (cls = opal_object_t_class.cls_next, cnext = cls->cls_next;
+ cls != &opal_object_t_class;
+ cls = cnext, cnext = cls->cls_next) {
+
+ opal_atomic_lock(&cls->cls_init_lock);
+ free(cls->cls_construct_array);
+
+#ifdef USE_UMEM
+ umem_cache_destroy(cls->cls_cache);
+#endif
+
+ /*
+ * Remove class from class list, this is safe, because of
the safe list
+ * traversal
+ */
+ cls->cls_prev->cls_next = cls->cls_next;
+ cls->cls_next->cls_prev = cls->cls_prev;
+ cls->cls_prev = cls->cls_next = NULL;
+ cls->cls_initialized = 0;
+ opal_atomic_unlock(&cls->cls_init_lock);
}
return OPAL_SUCCESS;
}
static void save_class(opal_class_t *cls)
{
- if (num_classes >= max_classes) {
- expand_array();
- }
+ opal_class_t *cnext, *cprev;
- classes[num_classes] = cls->cls_construct_array;
- ++num_classes;
-}
-
-
-static void expand_array(void)
-{
- int i;
-
- max_classes += increment;
- classes = (void**)realloc(classes, sizeof(void *) * max_classes);
- if (NULL == classes) {
- perror("class malloc failed");
- exit(-1);
- }
- for (i = num_classes; i < max_classes; ++i) {
- classes[i] = NULL;
- }
+ opal_atomic_lock(&class_lock);
+ cls->cls_next = cnext = &opal_object_t_class;
+ cls->cls_prev = cprev = opal_object_t_class.cls_prev;
+ cnext->cls_prev = cls;
+ cprev->cls_next = cls;
+ opal_atomic_unlock(&class_lock);
}
-
_______________________________________________
devel mailing list
de...@open-mpi.org
http://www.open-mpi.org/mailman/listinfo.cgi/devel