• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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