1 // ParkingLot is an internal API for building efficient synchronization 2 // primitives like mutexes and events. 3 // 4 // The API and name is inspired by WebKit's WTF::ParkingLot, which in turn 5 // is inspired Linux's futex API. 6 // See https://webkit.org/blog/6161/locking-in-webkit/. 7 // 8 // The core functionality is an atomic "compare-and-sleep" operation along with 9 // an atomic "wake-up" operation. 10 11 #ifndef Py_INTERNAL_PARKING_LOT_H 12 #define Py_INTERNAL_PARKING_LOT_H 13 #ifdef __cplusplus 14 extern "C" { 15 #endif 16 17 #ifndef Py_BUILD_CORE 18 # error "this header requires Py_BUILD_CORE define" 19 #endif 20 21 22 enum { 23 // The thread was unparked by another thread. 24 Py_PARK_OK = 0, 25 26 // The value of `address` did not match `expected`. 27 Py_PARK_AGAIN = -1, 28 29 // The thread was unparked due to a timeout. 30 Py_PARK_TIMEOUT = -2, 31 32 // The thread was interrupted by a signal. 33 Py_PARK_INTR = -3, 34 }; 35 36 // Checks that `*address == *expected` and puts the thread to sleep until an 37 // unpark operation is called on the same `address`. Otherwise, the function 38 // returns `Py_PARK_AGAIN`. The comparison behaves like memcmp, but is 39 // performed atomically with respect to unpark operations. 40 // 41 // The `address_size` argument is the size of the data pointed to by the 42 // `address` and `expected` pointers (i.e., sizeof(*address)). It must be 43 // 1, 2, 4, or 8. 44 // 45 // The `timeout_ns` argument specifies the maximum amount of time to wait, with 46 // -1 indicating an infinite wait. 47 // 48 // `park_arg`, which can be NULL, is passed to the unpark operation. 49 // 50 // If `detach` is true, then the thread will detach/release the GIL while 51 // waiting. 52 // 53 // Example usage: 54 // 55 // if (_Py_atomic_compare_exchange_uint8(address, &expected, new_value)) { 56 // int res = _PyParkingLot_Park(address, &new_value, sizeof(*address), 57 // timeout_ns, NULL, 1); 58 // ... 59 // } 60 PyAPI_FUNC(int) 61 _PyParkingLot_Park(const void *address, const void *expected, 62 size_t address_size, PyTime_t timeout_ns, 63 void *park_arg, int detach); 64 65 // Callback for _PyParkingLot_Unpark: 66 // 67 // `arg` is the data of the same name provided to the _PyParkingLot_Unpark() 68 // call. 69 // `park_arg` is the data provided to _PyParkingLot_Park() call or NULL if 70 // no waiting thread was found. 71 // `has_more_waiters` is true if there are more threads waiting on the same 72 // address. May be true in cases where threads are waiting on a different 73 // address that map to the same internal bucket. 74 typedef void _Py_unpark_fn_t(void *arg, void *park_arg, int has_more_waiters); 75 76 // Unparks a single thread waiting on `address`. 77 // 78 // Note that fn() is called regardless of whether a thread was unparked. If 79 // no threads are waiting on `address` then the `park_arg` argument to fn() 80 // will be NULL. 81 // 82 // Example usage: 83 // void callback(void *arg, void *park_arg, int has_more_waiters); 84 // _PyParkingLot_Unpark(address, &callback, arg); 85 PyAPI_FUNC(void) 86 _PyParkingLot_Unpark(const void *address, _Py_unpark_fn_t *fn, void *arg); 87 88 // Unparks all threads waiting on `address`. 89 PyAPI_FUNC(void) _PyParkingLot_UnparkAll(const void *address); 90 91 // Resets the parking lot state after a fork. Forgets all parked threads. 92 PyAPI_FUNC(void) _PyParkingLot_AfterFork(void); 93 94 #ifdef __cplusplus 95 } 96 #endif 97 #endif /* !Py_INTERNAL_PARKING_LOT_H */ 98