Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 10:47:20

0001 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
0002 /*
0003  * Copyright (c) 2012-2016 Cisco Systems, Inc.  All rights reserved.
0004  * Copyright (c) 2012      Los Alamos National Security, LLC. All rights reserved
0005  * Copyright (c) 2015-2020 Intel, Inc.  All rights reserved.
0006  * Copyright (c) 2019      Research Organization for Information Science
0007  *                         and Technology (RIST).  All rights reserved.
0008  * Copyright (c) 2020      IBM Corporation.  All rights reserved.
0009  * Copyright (c) 2021-2022 Nanook Consulting.  All rights reserved.
0010  * $COPYRIGHT$
0011  *
0012  * Additional copyrights may follow
0013  *
0014  * $HEADER$
0015  */
0016 
0017 /** @file
0018  *
0019  * This file provides a "hotel" class:
0020  *
0021  * - A hotel has a fixed number of rooms (i.e., storage slots)
0022  * - An arbitrary data pointer can check into an empty room at any time
0023  * - The occupant of a room can check out at any time
0024  * - Optionally, the occupant of a room can be forcibly evicted at a
0025  *   given time (i.e., when an pmix timer event expires).
0026  * - The hotel has finite occupancy; if you try to checkin a new
0027  *   occupant and the hotel is already full, it will gracefully fail
0028  *   to checkin.
0029  *
0030  * One use case for this class is for ACK-based network retransmission
0031  * schemes (NACK-based retransmission schemes probably can use
0032  * pmix_ring_buffer).
0033  *
0034  * For ACK-based retransmission schemes, a hotel might be used
0035  * something like this:
0036  *
0037  * - when a message is sent, check it in to a hotel with a timer
0038  * - if an ACK is received, check it out of the hotel (which also cancels
0039  *   the timer)
0040  * - if an ACK isn't received in time, the timer will expire and the
0041  *   upper layer will get a callback with the message
0042  * - if an ACK is received late (i.e., after its timer has expired),
0043  *   then checkout will gracefully fail
0044  *
0045  * Note that this class intentionally provides pretty minimal
0046  * functionality.  It is intended to be used in performance-critical
0047  * code paths -- extra functionality would simply add latency.
0048  *
0049  * There is an pmix_hotel_init() function to create a hotel, but no
0050  * corresponding finalize; the destructor will handle all finalization
0051  * issues.  Note that when a hotel is destroyed, it will delete all
0052  * pending events from the event base (i.e., all pending eviction
0053  * callbacks); no further eviction callbacks will be invoked.
0054  */
0055 
0056 #ifndef PMIX_HOTEL_H
0057 #define PMIX_HOTEL_H
0058 
0059 #include "src/include/pmix_config.h"
0060 #include "pmix_common.h"
0061 #include "src/class/pmix_object.h"
0062 #include "src/include/pmix_prefetch.h"
0063 #include "src/include/pmix_types.h"
0064 #include <event.h>
0065 
0066 #include "src/util/pmix_error.h"
0067 #include "src/util/pmix_output.h"
0068 
0069 BEGIN_C_DECLS
0070 
0071 struct pmix_hotel_t;
0072 
0073 /* User-supplied function to be invoked when an occupant is evicted. */
0074 typedef void (*pmix_hotel_eviction_callback_fn_t)(struct pmix_hotel_t *hotel, int room_num,
0075                                                   void *occupant);
0076 
0077 /* Note that this is an internal data structure; it is not part of the
0078    public pmix_hotel interface.  Public consumers of pmix_hotel
0079    shouldn't need to use this struct at all (we only have it here in
0080    this .h file because some functions are inlined for speed, and need
0081    to get to the internals of this struct).
0082 
0083    The room struct should be as small as possible to be cache
0084    friendly.  Specifically: it would be great if multiple rooms could
0085    fit in a single cache line because we'll always allocate a
0086    contiguous set of rooms in an array. */
0087 typedef struct {
0088     void *occupant;
0089     pmix_event_t eviction_timer_event;
0090 } pmix_hotel_room_t;
0091 
0092 /* Note that this is an internal data structure; it is not part of the
0093    public pmix_hotel interface.  Public consumers of pmix_hotel
0094    shouldn't need to use this struct at all (we only have it here in
0095    this .h file because some functions are inlined for speed, and need
0096    to get to the internals of this struct).
0097 
0098    Use a unique struct for holding the arguments for eviction
0099    callbacks.  We *could* make the to-be-evicted pmix_hotel_room_t
0100    instance as the argument, but we don't, for 2 reasons:
0101 
0102    1. We want as many pmix_hotel_room_t's to fit in a cache line as
0103       possible (i.e., to be as cache-friendly as possible).  The
0104       common/fast code path only needs to access the data in the
0105       pmix_hotel_room_t (and not the callback argument data).
0106 
0107    2. Evictions will be uncommon, so we don't mind penalizing them a
0108       bit by making the data be in a separate cache line.
0109 */
0110 typedef struct {
0111     struct pmix_hotel_t *hotel;
0112     int room_num;
0113 } pmix_hotel_room_eviction_callback_arg_t;
0114 
0115 typedef struct pmix_hotel_t {
0116     /* make this an object */
0117     pmix_object_t super;
0118 
0119     /* Max number of rooms in the hotel */
0120     int num_rooms;
0121 
0122     /* event base to be used for eviction timeout */
0123     pmix_event_base_t *evbase;
0124     struct timeval eviction_timeout;
0125     pmix_hotel_eviction_callback_fn_t evict_callback_fn;
0126 
0127     /* All rooms in this hotel */
0128     pmix_hotel_room_t *rooms;
0129 
0130     /* Separate array for all the eviction callback arguments (see
0131        rationale above for why this is a separate array) */
0132     pmix_hotel_room_eviction_callback_arg_t *eviction_args;
0133 
0134     /* All currently unoccupied rooms in this hotel (not necessarily
0135        in any particular order) */
0136     int *unoccupied_rooms;
0137     int last_unoccupied_room;
0138 } pmix_hotel_t;
0139 PMIX_EXPORT PMIX_CLASS_DECLARATION(pmix_hotel_t);
0140 
0141 #define PMIX_HOTEL_STATIC_INIT                      \
0142 {                                                   \
0143     .super = PMIX_OBJ_STATIC_INIT(pmix_object_t),   \
0144     .num_rooms = 0,                                 \
0145     .evbase = NULL,                                 \
0146     .eviction_timeout = {0, 0},                     \
0147     .evict_callback_fn = NULL,                      \
0148     .rooms = NULL,                                  \
0149     .eviction_args = NULL,                          \
0150     .unoccupied_rooms = NULL,                       \
0151     .last_unoccupied_room = 0                       \
0152 }
0153 
0154 
0155 /**
0156  * Initialize the hotel.
0157  *
0158  * @param hotel Pointer to a hotel (IN)
0159  * @param num_rooms The total number of rooms in the hotel (IN)
0160  * @param evbase Pointer to event base used for eviction timeout
0161  * @param eviction_timeout Max length of a stay at the hotel before
0162  * the eviction callback is invoked (in seconds)
0163  * @param evict_callback_fn Callback function invoked if an occupant
0164  * does not check out before the eviction_timeout.
0165  *
0166  * NOTE: If the callback function is NULL, then no eviction timer
0167  * will be set - occupants will remain checked into the hotel until
0168  * explicitly checked out.
0169  *
0170  * Also note: the eviction_callback_fn should absolutely not call any
0171  * of the hotel checkout functions.  Specifically: the occupant has
0172  * already been ("forcibly") checked out *before* the
0173  * eviction_callback_fn is invoked.
0174  *
0175  * @return PMIX_SUCCESS if all initializations were successful. Otherwise,
0176  *  the error indicate what went wrong in the function.
0177  */
0178 PMIX_EXPORT pmix_status_t pmix_hotel_init(pmix_hotel_t *hotel, int num_rooms,
0179                                           pmix_event_base_t *evbase, uint32_t eviction_timeout,
0180                                           pmix_hotel_eviction_callback_fn_t evict_callback_fn);
0181 
0182 /**
0183  * Check in an occupant to the hotel.
0184  *
0185  * @param hotel Pointer to hotel (IN)
0186  * @param occupant Occupant to check in (opaque to the hotel) (IN)
0187  * @param room The room number that identifies this occupant in the
0188  * hotel (OUT).
0189  *
0190  * If there is room in the hotel, the occupant is checked in and the
0191  * timer for that occupant is started.  The occupant's room is
0192  * returned in the "room" param.
0193  *
0194  * Note that once a room's checkout_expire timer expires, the occupant
0195  * is forcibly checked out, and then the eviction callback is invoked.
0196  *
0197  * @return PMIX_SUCCESS if the occupant is successfully checked in,
0198  * and the room parameter will contain a valid value.
0199  * @return PMIX_ERR_TEMP_OUT_OF_RESOURCE is the hotel is full.  Try
0200  * again later.
0201  */
0202 static inline pmix_status_t pmix_hotel_checkin(pmix_hotel_t *hotel, void *occupant, int *room_num)
0203 {
0204     pmix_hotel_room_t *room;
0205 
0206     /* Do we have any rooms available? */
0207     if (PMIX_UNLIKELY(hotel->last_unoccupied_room < 0)) {
0208         *room_num = -1;
0209         return PMIX_ERR_OUT_OF_RESOURCE;
0210     }
0211 
0212     /* Put this occupant into the first empty room that we have */
0213     *room_num = hotel->unoccupied_rooms[hotel->last_unoccupied_room--];
0214     room = &(hotel->rooms[*room_num]);
0215     room->occupant = occupant;
0216 
0217     /* Assign the event and make it pending */
0218     if (NULL != hotel->evbase) {
0219         pmix_event_add(&(room->eviction_timer_event), &(hotel->eviction_timeout));
0220     }
0221 
0222     return PMIX_SUCCESS;
0223 }
0224 
0225 /**
0226  * Same as pmix_hotel_checkin(), but slightly optimized for when the
0227  * caller *knows* that there is a room available.
0228  */
0229 static inline void pmix_hotel_checkin_with_res(pmix_hotel_t *hotel, void *occupant, int *room_num)
0230 {
0231     pmix_hotel_room_t *room;
0232 
0233     /* Put this occupant into the first empty room that we have */
0234     *room_num = hotel->unoccupied_rooms[hotel->last_unoccupied_room--];
0235     room = &(hotel->rooms[*room_num]);
0236     assert(room->occupant == NULL);
0237     room->occupant = occupant;
0238 
0239     /* Assign the event and make it pending */
0240     if (NULL != hotel->evbase) {
0241         pmix_event_add(&(room->eviction_timer_event), &(hotel->eviction_timeout));
0242     }
0243 }
0244 
0245 /**
0246  * Check the specified occupant out of the hotel.
0247  *
0248  * @param hotel Pointer to hotel (IN)
0249  * @param room Room number to checkout (IN)
0250  *
0251  * If there is an occupant in the room, their timer is canceled and
0252  * they are checked out.
0253  *
0254  * Nothing is returned (as a minor optimization).
0255  */
0256 static inline void pmix_hotel_checkout(pmix_hotel_t *hotel, int room_num)
0257 {
0258     pmix_hotel_room_t *room;
0259 
0260     /* Bozo check */
0261     assert(room_num < hotel->num_rooms);
0262     if (0 > room_num) {
0263         /* occupant wasn't checked in */
0264         return;
0265     }
0266 
0267     /* If there's an occupant in the room, check them out */
0268     room = &(hotel->rooms[room_num]);
0269     if (PMIX_LIKELY(NULL != room->occupant)) {
0270         /* Do not change this logic without also changing the same
0271            logic in pmix_hotel_checkout_and_return_occupant() and
0272            pmix_hotel.c:local_eviction_callback(). */
0273         room->occupant = NULL;
0274         if (NULL != hotel->evbase) {
0275             pmix_event_del(&(room->eviction_timer_event));
0276         }
0277         hotel->last_unoccupied_room++;
0278         assert(hotel->last_unoccupied_room < hotel->num_rooms);
0279         hotel->unoccupied_rooms[hotel->last_unoccupied_room] = room_num;
0280     }
0281 
0282     /* Don't bother returning whether we actually checked someone out
0283        or not (because this is in the critical performance path) --
0284        assume the upper layer knows what it's doing. */
0285 }
0286 
0287 /**
0288  * Check the specified occupant out of the hotel and return the occupant.
0289  *
0290  * @param hotel Pointer to hotel (IN)
0291  * @param room Room number to checkout (IN)
0292  * @param void * occupant (OUT)
0293  * If there is an occupant in the room, their timer is canceled and
0294  * they are checked out.
0295  *
0296  * Use this checkout and when caller needs the occupant
0297  */
0298 static inline void pmix_hotel_checkout_and_return_occupant(pmix_hotel_t *hotel, int room_num,
0299                                                            void **occupant)
0300 {
0301     pmix_hotel_room_t *room;
0302 
0303     /* Bozo check */
0304     assert(room_num < hotel->num_rooms);
0305     if (0 > room_num) {
0306         /* occupant wasn't checked in */
0307         *occupant = NULL;
0308         return;
0309     }
0310 
0311     /* If there's an occupant in the room, check them out */
0312     room = &(hotel->rooms[room_num]);
0313     if (PMIX_LIKELY(NULL != room->occupant)) {
0314         pmix_output(10, "checking out occupant %p from room num %d", room->occupant, room_num);
0315         /* Do not change this logic without also changing the same
0316            logic in pmix_hotel_checkout() and
0317            pmix_hotel.c:local_eviction_callback(). */
0318         *occupant = room->occupant;
0319         room->occupant = NULL;
0320         if (NULL != hotel->evbase) {
0321             pmix_event_del(&(room->eviction_timer_event));
0322         }
0323         hotel->last_unoccupied_room++;
0324         assert(hotel->last_unoccupied_room < hotel->num_rooms);
0325         hotel->unoccupied_rooms[hotel->last_unoccupied_room] = room_num;
0326     } else {
0327         *occupant = NULL;
0328     }
0329 }
0330 
0331 /**
0332  * Returns true if the hotel is empty (no occupant)
0333  * @param hotel Pointer to hotel (IN)
0334  * @return bool true if empty false if there is a occupant(s)
0335  *
0336  */
0337 static inline bool pmix_hotel_is_empty(pmix_hotel_t *hotel)
0338 {
0339     if (hotel->last_unoccupied_room == hotel->num_rooms - 1)
0340         return true;
0341     else
0342         return false;
0343 }
0344 
0345 /**
0346  * Access the occupant of a room, but leave them checked into their room.
0347  *
0348  * @param hotel Pointer to hotel (IN)
0349  * @param room Room number to checkout (IN)
0350  * @param void * occupant (OUT)
0351  *
0352  * This accessor function is typically used to cycle across the occupants
0353  * to check for someone already present that matches a description.
0354  */
0355 static inline void pmix_hotel_knock(pmix_hotel_t *hotel, int room_num, void **occupant)
0356 {
0357     pmix_hotel_room_t *room;
0358 
0359     /* Bozo check */
0360     assert(room_num < hotel->num_rooms);
0361 
0362     *occupant = NULL;
0363     if (0 > room_num) {
0364         /* occupant wasn't checked in */
0365         return;
0366     }
0367 
0368     /* If there's an occupant in the room, have them come to the door */
0369     room = &(hotel->rooms[room_num]);
0370     if (PMIX_LIKELY(NULL != room->occupant)) {
0371         pmix_output(10, "occupant %p in room num %d responded to knock", room->occupant, room_num);
0372         *occupant = room->occupant;
0373     }
0374 }
0375 
0376 END_C_DECLS
0377 
0378 #endif /* PMIX_HOTEL_H */