![]() |
|
|||
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 */
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
![]() ![]() |