root/opal/util/info_subscriber.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. infosubscriber_construct
  2. infosubscriber_destruct
  3. opal_callback_list_item_destruct
  4. opal_infosubscribe_inform_subscribers
  5. opal_infosubscribe_testcallback
  6. opal_infosubscribe_testregister
  7. save_original_key_val
  8. opal_infosubscribe_change_info
  9. opal_infosubscribe_subscribe

   1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
   2 /*
   3  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
   4  *                         University Research and Technology
   5  *                         Corporation.  All rights reserved.
   6  * Copyright (c) 2004-2007 The University of Tennessee and The University
   7  *                         of Tennessee Research Foundation.  All rights
   8  *                         reserved.
   9  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
  10  *                         University of Stuttgart.  All rights reserved.
  11  * Copyright (c) 2004-2005 The Regents of the University of California.
  12  *                         All rights reserved.
  13  * Copyright (c) 2007-2018 Cisco Systems, Inc.  All rights reserved
  14  * Copyright (c) 2009      Sun Microsystems, Inc.  All rights reserved.
  15  * Copyright (c) 2012-2017 Los Alamos National Security, LLC. All rights
  16  *                         reserved.
  17  * Copyright (c) 2015-2018 Research Organization for Information Science
  18  *                         and Technology (RIST). All rights reserved.
  19  * Copyright (c) 2016-2018 IBM Corporation. All rights reserved.
  20  * Copyright (c) 2017-2018 Intel, Inc. All rights reserved.
  21  * $COPYRIGHT$
  22  *
  23  * Additional copyrights may follow
  24  *
  25  * $HEADER$
  26  */
  27 
  28 #include <string.h>
  29 #include <errno.h>
  30 #include <stdlib.h>
  31 #ifdef HAVE_UNISTD_H
  32 #include <unistd.h>
  33 #endif
  34 #include <limits.h>
  35 #include <ctype.h>
  36 #ifdef HAVE_SYS_UTSNAME_H
  37 #include <sys/utsname.h>
  38 #endif
  39 #include <assert.h>
  40 
  41 #include "opal/util/argv.h"
  42 #include "opal/util/opal_getcwd.h"
  43 #include "opal/util/output.h"
  44 #include "opal/util/info_subscriber.h"
  45 
  46 static char* opal_infosubscribe_inform_subscribers(opal_infosubscriber_t * object, char *key, char *new_value, int *found_callback);
  47 static void infosubscriber_construct(opal_infosubscriber_t *obj);
  48 static void infosubscriber_destruct(opal_infosubscriber_t *obj);
  49 
  50 /*
  51  * Local structures
  52  */
  53 
  54 typedef struct opal_callback_list_t opal_callback_list_t;
  55 
  56 struct opal_callback_list_item_t {
  57     opal_list_item_t super;
  58     char *default_value;
  59     opal_key_interest_callback_t *callback;
  60 };
  61 typedef struct opal_callback_list_item_t opal_callback_list_item_t;
  62 
  63 OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_infosubscriber_t);
  64 OBJ_CLASS_INSTANCE(opal_infosubscriber_t,
  65                    opal_object_t,
  66                    infosubscriber_construct,
  67                    infosubscriber_destruct);
  68 
  69 OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_callback_list_item_t);
  70 static void opal_callback_list_item_destruct(opal_callback_list_item_t *obj);
  71 OBJ_CLASS_INSTANCE(opal_callback_list_item_t,
  72                    opal_list_item_t,
  73                    NULL,
  74                    opal_callback_list_item_destruct);
  75 
  76 static void infosubscriber_construct(opal_infosubscriber_t *obj) {
  77     OBJ_CONSTRUCT(&obj->s_subscriber_table, opal_hash_table_t);
  78     opal_hash_table_init(&obj->s_subscriber_table, 10);
  79 }
  80 
  81 static void infosubscriber_destruct(opal_infosubscriber_t *obj) {
  82     opal_hash_table_t *table = &obj->s_subscriber_table;
  83     void *node = NULL;
  84     int err;
  85     char *next_key;
  86     size_t key_size;
  87     opal_list_t *list = NULL;
  88 
  89     err = opal_hash_table_get_first_key_ptr(table,
  90         (void**) &next_key, &key_size, (void**) &list, &node);
  91     while (list && err == OPAL_SUCCESS) {
  92         OPAL_LIST_RELEASE(list);
  93 
  94         err = opal_hash_table_get_next_key_ptr(table,
  95           (void**) &next_key, &key_size, (void**) &list, node, &node);
  96     }
  97 
  98     OBJ_DESTRUCT(&obj->s_subscriber_table);
  99 
 100     if (NULL != obj->s_info) {
 101         OBJ_RELEASE(obj->s_info);
 102     }
 103 }
 104 
 105 static void opal_callback_list_item_destruct(opal_callback_list_item_t *obj) {
 106     if (obj->default_value) {
 107         free(obj->default_value); // came from a strdup()
 108     }
 109 }
 110 
 111 static char* opal_infosubscribe_inform_subscribers(opal_infosubscriber_t *object, char *key, char *new_value, int *found_callback)
 112 {
 113     opal_hash_table_t *table = &object->s_subscriber_table;
 114     opal_list_t *list = NULL;
 115     opal_callback_list_item_t *item;
 116     char *updated_value = NULL;
 117 
 118     if (found_callback) { *found_callback = 0; }
 119 /*
 120  * Present the new value to each subscriber.  They can decide to accept it, ignore it, or
 121  * over-ride it with their own value (like ignore, but they specify what value they want it to have).
 122  *
 123  * Since multiple subscribers could set values, only the last setting is kept as the
 124  * returned value.
 125  */
 126     if (table) {
 127         opal_hash_table_get_value_ptr(table, key, strlen(key), (void**) &list);
 128 
 129         if (list) {
 130             updated_value = new_value;
 131             OPAL_LIST_FOREACH(item, list, opal_callback_list_item_t) {
 132                 updated_value = item->callback(object, key, updated_value);
 133                 if (found_callback) { *found_callback = 1; }
 134             }
 135         }
 136     }
 137 
 138     return updated_value;
 139 }
 140 
 141 
 142 
 143 
 144 /*
 145  * Testing-only static data, all paths using this code should be
 146  * inactive in a normal run.  In particular ntesting_callbacks is 0
 147  * unless testing is in play.
 148  */
 149 static int ntesting_callbacks = 0;
 150 static opal_key_interest_callback_t *testing_callbacks[5];
 151 static char *testing_keys[5];
 152 static char *testing_initialvals[5];
 153 // User-level call, user adds their own callback function to be subscribed
 154 // to every object:
 155 int opal_infosubscribe_testcallback(opal_key_interest_callback_t *callback,
 156   char *key, char *val);
 157 
 158 int
 159 opal_infosubscribe_testcallback(opal_key_interest_callback_t *callback,
 160   char *key, char *val)
 161 {
 162     int i = ntesting_callbacks;
 163     if (ntesting_callbacks >= 5) { return -1; }
 164 
 165     testing_callbacks[i] = callback;
 166     testing_keys[i] = key;
 167     testing_initialvals[i] = val;
 168     ++ntesting_callbacks;
 169     return 0;
 170 }
 171 
 172 int opal_infosubscribe_testregister(opal_infosubscriber_t *object);
 173 int
 174 opal_infosubscribe_testregister(opal_infosubscriber_t *object)
 175 {
 176     opal_hash_table_t *table = &object->s_subscriber_table;
 177     opal_callback_list_item_t *item;
 178     opal_list_t *list = NULL;
 179 
 180 // The testing section should only ever be activated if the testing callback
 181 // above is used.
 182     if (ntesting_callbacks != 0) {
 183         int i;
 184         for (i=0; i<ntesting_callbacks; ++i) {
 185 // The testing-code only wants to add test-subscriptions
 186 // once for an obj.  So before adding a test-subscription, see
 187 // if it's already there.
 188             int found = 0;
 189             opal_hash_table_get_value_ptr(table, testing_keys[i],
 190                 strlen(testing_keys[i]), (void**) &list);
 191             if (list) {
 192                 OPAL_LIST_FOREACH(item, list, opal_callback_list_item_t) {
 193                     if (0 ==
 194                         strcmp(item->default_value, testing_initialvals[i])
 195                         &&
 196                         item->callback == testing_callbacks[i])
 197                     {
 198                         found = 1;
 199                     }
 200                 }
 201             }
 202             list = NULL;
 203 
 204             if (!found) {
 205                 opal_infosubscribe_subscribe(object,
 206                     testing_keys[i],
 207                     testing_initialvals[i], testing_callbacks[i]);
 208             }
 209         }
 210     }
 211 
 212 // For testing-mode only, while we're here, lets walk the whole list
 213 // to see if there are any duplicates.
 214     if (ntesting_callbacks != 0) {
 215         int err;
 216         void *node = NULL;
 217         size_t key_size;
 218         char *next_key;
 219         opal_callback_list_item_t *item1, *item2;
 220 
 221         err = opal_hash_table_get_first_key_ptr(table, (void**) &next_key,
 222             &key_size, (void**) &list, &node);
 223         while (list && err == OPAL_SUCCESS) {
 224             int counter = 0;
 225             OPAL_LIST_FOREACH(item1, list, opal_callback_list_item_t) {
 226                 OPAL_LIST_FOREACH(item2, list, opal_callback_list_item_t) {
 227                     if (0 ==
 228                         strcmp(item1->default_value, item2->default_value)
 229                         &&
 230                         item1->callback == item2->callback)
 231                     {
 232                         ++counter;
 233                     }
 234                 }
 235             }
 236             if (counter > 1) {
 237                 printf("ERROR: duplicate info key/val subscription found "
 238                     "in hash table\n");
 239                 exit(-1);
 240             }
 241 
 242             err = opal_hash_table_get_next_key_ptr(table,
 243                 (void**) &next_key, &key_size, (void**) &list, node, &node);
 244         }
 245     }
 246 
 247     return OPAL_SUCCESS;
 248 }
 249 
 250 // This routine is to be used after making a callback for a
 251 // key/val pair. The callback would have ggiven a new value to associate
 252 // with <key>, and this function saves the previous value under
 253 // __IN_<key>.
 254 //
 255 // The last argument indicates whether to overwrite a previous
 256 // __IN_<key> or not.
 257 static int
 258 save_original_key_val(opal_info_t *info, char *key, char *val, int overwrite)
 259 {
 260     char modkey[OPAL_MAX_INFO_KEY];
 261     int flag, err;
 262 
 263     // Checking strlen, even though it should be unnecessary.
 264     // This should only happen on predefined keys with short lengths.
 265     if (strlen(key) + strlen(OPAL_INFO_SAVE_PREFIX) < OPAL_MAX_INFO_KEY) {
 266         snprintf(modkey, OPAL_MAX_INFO_KEY,
 267             OPAL_INFO_SAVE_PREFIX "%s", key);
 268 // (the prefix macro is a string, so the unreadable part above is a string concatenation)
 269         flag = 0;
 270         opal_info_get(info, modkey, 0, NULL, &flag);
 271         if (!flag || overwrite) {
 272             err = opal_info_set(info, modkey, val);
 273             if (OPAL_SUCCESS != err) {
 274                 return err;
 275             }
 276         }
 277 // FIXME: use whatever the Open MPI convention is for DEBUG options like this
 278 // Even though I don't expect this codepath to happen, if it somehow DID happen
 279 // in a real run with user-keys, I'd rather it be silent at that point rather
 280 // being noisy and/or aborting.
 281 #ifdef OMPI_DEBUG
 282     } else {
 283         printf("WARNING: Unexpected key length [%s]\n", key);
 284 #endif
 285     }
 286     return OPAL_SUCCESS;
 287 }
 288 
 289 int
 290 opal_infosubscribe_change_info(opal_infosubscriber_t *object, opal_info_t *new_info)
 291 {
 292     int err;
 293     opal_info_entry_t *iterator;
 294     char *updated_value;
 295 
 296     /* for each key/value in new info, let subscribers know of new value */
 297     int found_callback;
 298 
 299     if (!object->s_info) {
 300         object->s_info = OBJ_NEW(opal_info_t);
 301     }
 302 
 303     if (NULL != new_info) {
 304     OPAL_LIST_FOREACH(iterator, &new_info->super, opal_info_entry_t) {
 305 
 306         updated_value = opal_infosubscribe_inform_subscribers(object, iterator->ie_key, iterator->ie_value, &found_callback);
 307         if (updated_value) {
 308             err = opal_info_set(object->s_info, iterator->ie_key, updated_value);
 309         } else {
 310 // This path would happen if there was no callback for this key,
 311 // or if there was a callback and it returned null. One way the
 312 // setting was unrecognized the other way it was recognized and ignored,
 313 // either way it shouldn't be set, which we'll ensure with an unset
 314 // in case a previous value exists.
 315             err = opal_info_delete(object->s_info, iterator->ie_key);
 316             err = OPAL_SUCCESS; // we don't care if the key was found or not
 317         }
 318         if (OPAL_SUCCESS != err) {
 319             return err;
 320         }
 321 // Save the original at "__IN_<key>":"original"
 322 // And if multiple set-info calls happen, the last would be the most relevant
 323 // to save, so overwrite a previously saved value if there is one.
 324         save_original_key_val(object->s_info,
 325             iterator->ie_key, iterator->ie_value, 1);
 326     }}
 327 
 328     return OPAL_SUCCESS;
 329 }
 330 
 331 // Callers can provide a callback for processing info k/v pairs.
 332 //
 333 // Currently the callback() is expected to return a static string, and the
 334 // callers of callback() do not try to free the string it returns. for example
 335 // current callbacks do things like
 336 //     return some_condition ? "true" : "false";
 337 // the caller of callback() uses the return value in an opal_info_set() which
 338 // strdups the string. The string returned from callback() is not kept beyond
 339 // that. Currently if the callback() did malloc/strdup/etc for its return value
 340 // the caller of callback() would have no way to know whether it needed freeing
 341 // or not, so that string would be leaked.
 342 //
 343 // For future consideration I'd propose a model where callback() is expected
 344 // to always strdup() its return value, so the value returned by callback()
 345 // would either be NULL or it would be a string that needs free()ed. It seems
 346 // to me this might be required if the strings become more dynamic than the
 347 // simple true/false values seen in the current code. It'll be an easy change,
 348 // callback() is only used two places.
 349 int opal_infosubscribe_subscribe(opal_infosubscriber_t *object, char *key, char *value, opal_key_interest_callback_t *callback)
 350 {
 351     opal_list_t *list = NULL;
 352     opal_hash_table_t *table = &object->s_subscriber_table;
 353     opal_callback_list_item_t *callback_list_item;
 354     size_t max_len = OPAL_MAX_INFO_KEY - strlen(OPAL_INFO_SAVE_PREFIX);
 355 
 356     if (strlen(key) > max_len) {
 357         opal_output(0, "DEVELOPER WARNING: Unexpected MPI info key length [%s]: "
 358                     "OMPI internal callback keys are limited to %" PRIsize_t " chars.",
 359                     key, max_len);
 360 #if OPAL_ENABLE_DEBUG
 361         opal_output(0, "Aborting because this is a developer / debugging build.  Go fix this error.");
 362         // Do not assert() / dump core.  Just exit un-gracefully.
 363         exit(1);
 364 #else
 365         opal_output(0, "The \"%s\" MPI info key almost certainly will not work properly.  You should inform an Open MPI developer about this.", key);
 366         key[max_len] = '\0';
 367 #endif
 368     }
 369 
 370     if (table) {
 371         opal_hash_table_get_value_ptr(table, key, strlen(key), (void**) &list);
 372 
 373         if (!list) {
 374             list = OBJ_NEW(opal_list_t);
 375             opal_hash_table_set_value_ptr(table, key, strlen(key), list);
 376         }
 377 
 378         callback_list_item = OBJ_NEW(opal_callback_list_item_t);
 379         callback_list_item->callback = callback;
 380         if (value) {
 381             callback_list_item->default_value = strdup(value);
 382         } else {
 383             callback_list_item->default_value = NULL;
 384         }
 385 
 386         opal_list_append(list, (opal_list_item_t*) callback_list_item);
 387 
 388 // Trigger callback() on either the default value or the info that's in the
 389 // object if there is one. Unfortunately there's some code duplication as
 390 // this is similar to the job of opal_infosubscribe_change_info().
 391 //
 392 // The value we store for key is whatever the callback() returns.
 393 // We also leave a backup __IN_* key with the previous value.
 394 
 395 //  - is there an info object yet attached to this object
 396         if (NULL == object->s_info) {
 397             object->s_info = OBJ_NEW(opal_info_t);
 398         }
 399 // - is there a value already associated with key in this obj's info:
 400 //   to use in the callback()
 401         char *buffer = malloc(OPAL_MAX_INFO_VAL+1); // (+1 shouldn't be needed)
 402         char *val = value; // start as default value
 403         int flag = 0;
 404         char *updated_value;
 405         int err;
 406         opal_info_get(object->s_info, key, OPAL_MAX_INFO_VAL, buffer, &flag);
 407         if (flag) {
 408             val = buffer; // become info value if this key was in info
 409         }
 410 // - callback() and modify the val in info
 411         updated_value = callback(object, key, val);
 412         if (updated_value) {
 413             err = opal_info_set(object->s_info, key, updated_value);
 414         } else {
 415             err = opal_info_delete(object->s_info, key);
 416             err = OPAL_SUCCESS; // we don't care if the key was found or not
 417         }
 418         if (OPAL_SUCCESS != err) {
 419             free(buffer);
 420             return err;
 421         }
 422 // - save the previous val under key __IN_*
 423 //   This function might be called separately for the same key multiple
 424 //   times (multiple modules might register an interest in the same key),
 425 //   so we only save __IN_<key> for the first.
 426 //   Note we're saving the first k/v regardless of whether it was the default
 427 //   or whether it came from info. This means system settings will show
 428 //   up if the user queries later with get_info.
 429         save_original_key_val(object->s_info, key, val, 0);
 430 
 431         free(buffer);
 432     } else {
 433 /*
 434  * TODO: This should not happen
 435  */
 436     }
 437 
 438     return OPAL_SUCCESS;
 439 }
 440 
 441 /*
 442     OBJ_DESTRUCT(&opal_comm_info_hashtable);
 443     OBJ_DESTRUCT(&opal_win_info_hashtable);
 444     OBJ_DESTRUCT(&opal_file_info_hashtable);
 445 */

/* [<][>][^][v][top][bottom][index][help] */