root/opal/mca/pmix/pmix4x/pmix/src/class/pmix_hotel.h

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

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. pmix_hotel_checkin
  2. pmix_hotel_checkin_with_res
  3. pmix_hotel_checkout
  4. pmix_hotel_checkout_and_return_occupant
  5. pmix_hotel_is_empty
  6. pmix_hotel_knock

   1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
   2 /*
   3  * Copyright (c) 2012-2016 Cisco Systems, Inc.  All rights reserved.
   4  * Copyright (c) 2012      Los Alamos National Security, LLC. All rights reserved
   5  * Copyright (c) 2015-2019 Intel, Inc.  All rights reserved.
   6  * Copyright (c) 2019      Research Organization for Information Science
   7  *                         and Technology (RIST).  All rights reserved.
   8  * $COPYRIGHT$
   9  *
  10  * Additional copyrights may follow
  11  *
  12  * $HEADER$
  13  */
  14 
  15 /** @file
  16  *
  17  * This file provides a "hotel" class:
  18  *
  19  * - A hotel has a fixed number of rooms (i.e., storage slots)
  20  * - An arbitrary data pointer can check into an empty room at any time
  21  * - The occupant of a room can check out at any time
  22  * - Optionally, the occupant of a room can be forcibly evicted at a
  23  *   given time (i.e., when an pmix timer event expires).
  24  * - The hotel has finite occupancy; if you try to checkin a new
  25  *   occupant and the hotel is already full, it will gracefully fail
  26  *   to checkin.
  27  *
  28  * One use case for this class is for ACK-based network retransmission
  29  * schemes (NACK-based retransmission schemes probably can use
  30  * pmix_ring_buffer).
  31  *
  32  * For ACK-based retransmission schemes, a hotel might be used
  33  * something like this:
  34  *
  35  * - when a message is sent, check it in to a hotel with a timer
  36  * - if an ACK is received, check it out of the hotel (which also cancels
  37  *   the timer)
  38  * - if an ACK isn't received in time, the timer will expire and the
  39  *   upper layer will get a callback with the message
  40  * - if an ACK is received late (i.e., after its timer has expired),
  41  *   then checkout will gracefully fail
  42  *
  43  * Note that this class intentionally provides pretty minimal
  44  * functionality.  It is intended to be used in performance-critical
  45  * code paths -- extra functionality would simply add latency.
  46  *
  47  * There is an pmix_hotel_init() function to create a hotel, but no
  48  * corresponding finalize; the destructor will handle all finalization
  49  * issues.  Note that when a hotel is destroyed, it will delete all
  50  * pending events from the event base (i.e., all pending eviction
  51  * callbacks); no further eviction callbacks will be invoked.
  52  */
  53 
  54 #ifndef PMIX_HOTEL_H
  55 #define PMIX_HOTEL_H
  56 
  57 #include <src/include/pmix_config.h>
  58 #include "src/include/prefetch.h"
  59 #include "pmix_common.h"
  60 #include "src/include/types.h"
  61 #include "src/class/pmix_object.h"
  62 #include PMIX_EVENT_HEADER
  63 
  64 #include "src/util/output.h"
  65 
  66 BEGIN_C_DECLS
  67 
  68 struct pmix_hotel_t;
  69 
  70 /* User-supplied function to be invoked when an occupant is evicted. */
  71 typedef void (*pmix_hotel_eviction_callback_fn_t)(struct pmix_hotel_t *hotel,
  72                                                   int room_num,
  73                                                   void *occupant);
  74 
  75 /* Note that this is an internal data structure; it is not part of the
  76    public pmix_hotel interface.  Public consumers of pmix_hotel
  77    shouldn't need to use this struct at all (we only have it here in
  78    this .h file because some functions are inlined for speed, and need
  79    to get to the internals of this struct).
  80 
  81    The room struct should be as small as possible to be cache
  82    friendly.  Specifically: it would be great if multiple rooms could
  83    fit in a single cache line because we'll always allocate a
  84    contiguous set of rooms in an array. */
  85 typedef struct {
  86     void *occupant;
  87     pmix_event_t eviction_timer_event;
  88 } pmix_hotel_room_t;
  89 
  90 /* Note that this is an internal data structure; it is not part of the
  91    public pmix_hotel interface.  Public consumers of pmix_hotel
  92    shouldn't need to use this struct at all (we only have it here in
  93    this .h file because some functions are inlined for speed, and need
  94    to get to the internals of this struct).
  95 
  96    Use a unique struct for holding the arguments for eviction
  97    callbacks.  We *could* make the to-be-evicted pmix_hotel_room_t
  98    instance as the argument, but we don't, for 2 reasons:
  99 
 100    1. We want as many pmix_hotel_room_t's to fit in a cache line as
 101       possible (i.e., to be as cache-friendly as possible).  The
 102       common/fast code path only needs to access the data in the
 103       pmix_hotel_room_t (and not the callback argument data).
 104 
 105    2. Evictions will be uncommon, so we don't mind penalizing them a
 106       bit by making the data be in a separate cache line.
 107 */
 108 typedef struct {
 109     struct pmix_hotel_t *hotel;
 110     int room_num;
 111 } pmix_hotel_room_eviction_callback_arg_t;
 112 
 113 typedef struct pmix_hotel_t {
 114     /* make this an object */
 115     pmix_object_t super;
 116 
 117     /* Max number of rooms in the hotel */
 118     int num_rooms;
 119 
 120     /* event base to be used for eviction timeout */
 121     pmix_event_base_t *evbase;
 122     struct timeval eviction_timeout;
 123     pmix_hotel_eviction_callback_fn_t evict_callback_fn;
 124 
 125     /* All rooms in this hotel */
 126     pmix_hotel_room_t *rooms;
 127 
 128     /* Separate array for all the eviction callback arguments (see
 129        rationale above for why this is a separate array) */
 130     pmix_hotel_room_eviction_callback_arg_t *eviction_args;
 131 
 132     /* All currently unoccupied rooms in this hotel (not necessarily
 133        in any particular order) */
 134     int *unoccupied_rooms;
 135     int last_unoccupied_room;
 136 } pmix_hotel_t;
 137 PMIX_CLASS_DECLARATION(pmix_hotel_t);
 138 
 139 /**
 140  * Initialize the hotel.
 141  *
 142  * @param hotel Pointer to a hotel (IN)
 143  * @param num_rooms The total number of rooms in the hotel (IN)
 144  * @param evbase Pointer to event base used for eviction timeout
 145  * @param eviction_timeout Max length of a stay at the hotel before
 146  * the eviction callback is invoked (in microseconds)
 147  * @param evict_callback_fn Callback function invoked if an occupant
 148  * does not check out before the eviction_timeout.
 149  *
 150  * NOTE: If the callback function is NULL, then no eviction timer
 151  * will be set - occupants will remain checked into the hotel until
 152  * explicitly checked out.
 153  *
 154  * Also note: the eviction_callback_fn should absolutely not call any
 155  * of the hotel checkout functions.  Specifically: the occupant has
 156  * already been ("forcibly") checked out *before* the
 157  * eviction_callback_fn is invoked.
 158  *
 159  * @return PMIX_SUCCESS if all initializations were succesful. Otherwise,
 160  *  the error indicate what went wrong in the function.
 161  */
 162 PMIX_EXPORT pmix_status_t pmix_hotel_init(pmix_hotel_t *hotel, int num_rooms,
 163                                           pmix_event_base_t *evbase,
 164                                           uint32_t eviction_timeout,
 165                                           pmix_hotel_eviction_callback_fn_t evict_callback_fn);
 166 
 167 /**
 168  * Check in an occupant to the hotel.
 169  *
 170  * @param hotel Pointer to hotel (IN)
 171  * @param occupant Occupant to check in (opaque to the hotel) (IN)
 172  * @param room The room number that identifies this occupant in the
 173  * hotel (OUT).
 174  *
 175  * If there is room in the hotel, the occupant is checked in and the
 176  * timer for that occupant is started.  The occupant's room is
 177  * returned in the "room" param.
 178  *
 179  * Note that once a room's checkout_expire timer expires, the occupant
 180  * is forcibly checked out, and then the eviction callback is invoked.
 181  *
 182  * @return PMIX_SUCCESS if the occupant is successfully checked in,
 183  * and the room parameter will contain a valid value.
 184  * @return PMIX_ERR_TEMP_OUT_OF_RESOURCE is the hotel is full.  Try
 185  * again later.
 186  */
 187 static inline pmix_status_t pmix_hotel_checkin(pmix_hotel_t *hotel,
 188                                                void *occupant,
 189                                                int *room_num)
 190 {
 191     pmix_hotel_room_t *room;
 192 
 193     /* Do we have any rooms available? */
 194     if (PMIX_UNLIKELY(hotel->last_unoccupied_room < 0)) {
 195         *room_num = -1;
 196         return PMIX_ERR_OUT_OF_RESOURCE;
 197     }
 198 
 199     /* Put this occupant into the first empty room that we have */
 200     *room_num = hotel->unoccupied_rooms[hotel->last_unoccupied_room--];
 201     room = &(hotel->rooms[*room_num]);
 202     room->occupant = occupant;
 203 
 204     /* Assign the event and make it pending */
 205     if (NULL != hotel->evbase) {
 206         pmix_event_add(&(room->eviction_timer_event),
 207                        &(hotel->eviction_timeout));
 208     }
 209 
 210     return PMIX_SUCCESS;
 211 }
 212 
 213 /**
 214  * Same as pmix_hotel_checkin(), but slightly optimized for when the
 215  * caller *knows* that there is a room available.
 216  */
 217 static inline void pmix_hotel_checkin_with_res(pmix_hotel_t *hotel,
 218                                                void *occupant,
 219                                                int *room_num)
 220 {
 221     pmix_hotel_room_t *room;
 222 
 223     /* Put this occupant into the first empty room that we have */
 224     *room_num = hotel->unoccupied_rooms[hotel->last_unoccupied_room--];
 225     room = &(hotel->rooms[*room_num]);
 226     assert(room->occupant == NULL);
 227     room->occupant = occupant;
 228 
 229     /* Assign the event and make it pending */
 230     if (NULL != hotel->evbase) {
 231         pmix_event_add(&(room->eviction_timer_event),
 232                        &(hotel->eviction_timeout));
 233     }
 234 }
 235 
 236 /**
 237  * Check the specified occupant out of the hotel.
 238  *
 239  * @param hotel Pointer to hotel (IN)
 240  * @param room Room number to checkout (IN)
 241  *
 242  * If there is an occupant in the room, their timer is canceled and
 243  * they are checked out.
 244  *
 245  * Nothing is returned (as a minor optimization).
 246  */
 247 static inline void pmix_hotel_checkout(pmix_hotel_t *hotel, int room_num)
 248 {
 249     pmix_hotel_room_t *room;
 250 
 251     /* Bozo check */
 252     assert(room_num < hotel->num_rooms);
 253     if (0 > room_num) {
 254         /* occupant wasn't checked in */
 255         return;
 256     }
 257 
 258     /* If there's an occupant in the room, check them out */
 259     room = &(hotel->rooms[room_num]);
 260     if (PMIX_LIKELY(NULL != room->occupant)) {
 261         /* Do not change this logic without also changing the same
 262            logic in pmix_hotel_checkout_and_return_occupant() and
 263            pmix_hotel.c:local_eviction_callback(). */
 264         room->occupant = NULL;
 265         if (NULL != hotel->evbase) {
 266             pmix_event_del(&(room->eviction_timer_event));
 267         }
 268         hotel->last_unoccupied_room++;
 269         assert(hotel->last_unoccupied_room < hotel->num_rooms);
 270         hotel->unoccupied_rooms[hotel->last_unoccupied_room] = room_num;
 271     }
 272 
 273     /* Don't bother returning whether we actually checked someone out
 274        or not (because this is in the critical performance path) --
 275        assume the upper layer knows what it's doing. */
 276 }
 277 
 278 /**
 279  * Check the specified occupant out of the hotel and return the occupant.
 280  *
 281  * @param hotel Pointer to hotel (IN)
 282  * @param room Room number to checkout (IN)
 283  * @param void * occupant (OUT)
 284  * If there is an occupant in the room, their timer is canceled and
 285  * they are checked out.
 286  *
 287  * Use this checkout and when caller needs the occupant
 288  */
 289 static inline void pmix_hotel_checkout_and_return_occupant(pmix_hotel_t *hotel, int room_num, void **occupant)
 290 {
 291     pmix_hotel_room_t *room;
 292 
 293     /* Bozo check */
 294     assert(room_num < hotel->num_rooms);
 295     if (0 > room_num) {
 296         /* occupant wasn't checked in */
 297         *occupant = NULL;
 298         return;
 299     }
 300 
 301     /* If there's an occupant in the room, check them out */
 302     room = &(hotel->rooms[room_num]);
 303     if (PMIX_LIKELY(NULL != room->occupant)) {
 304         pmix_output (10, "checking out occupant %p from room num %d", room->occupant, room_num);
 305         /* Do not change this logic without also changing the same
 306            logic in pmix_hotel_checkout() and
 307            pmix_hotel.c:local_eviction_callback(). */
 308         *occupant = room->occupant;
 309         room->occupant = NULL;
 310         if (NULL != hotel->evbase) {
 311             pmix_event_del(&(room->eviction_timer_event));
 312         }
 313         hotel->last_unoccupied_room++;
 314         assert(hotel->last_unoccupied_room < hotel->num_rooms);
 315         hotel->unoccupied_rooms[hotel->last_unoccupied_room] = room_num;
 316     }
 317     else {
 318         *occupant = NULL;
 319     }
 320 }
 321 
 322 /**
 323  * Returns true if the hotel is empty (no occupant)
 324  * @param hotel Pointer to hotel (IN)
 325  * @return bool true if empty false if there is a occupant(s)
 326  *
 327  */
 328 static inline bool pmix_hotel_is_empty (pmix_hotel_t *hotel)
 329 {
 330     if (hotel->last_unoccupied_room == hotel->num_rooms - 1)
 331         return true;
 332     else
 333         return false;
 334 }
 335 
 336 /**
 337  * Access the occupant of a room, but leave them checked into their room.
 338  *
 339  * @param hotel Pointer to hotel (IN)
 340  * @param room Room number to checkout (IN)
 341  * @param void * occupant (OUT)
 342  *
 343  * This accessor function is typically used to cycle across the occupants
 344  * to check for someone already present that matches a description.
 345  */
 346 static inline void pmix_hotel_knock(pmix_hotel_t *hotel, int room_num, void **occupant)
 347 {
 348     pmix_hotel_room_t *room;
 349 
 350     /* Bozo check */
 351     assert(room_num < hotel->num_rooms);
 352 
 353     *occupant = NULL;
 354     if (0 > room_num) {
 355         /* occupant wasn't checked in */
 356         return;
 357     }
 358 
 359     /* If there's an occupant in the room, have them come to the door */
 360     room = &(hotel->rooms[room_num]);
 361     if (PMIX_LIKELY(NULL != room->occupant)) {
 362         pmix_output (10, "occupant %p in room num %d responded to knock", room->occupant, room_num);
 363         *occupant = room->occupant;
 364     }
 365 }
 366 
 367 END_C_DECLS
 368 
 369 #endif /* PMIX_HOTEL_H */

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