39 namespace discord_core_internal {
41 struct rate_limit_data {
42 friend class https_connection_stack_holder;
43 friend class https_connection_manager;
44 friend class rate_limit_stack_holder;
45 friend class https_rnr_builder;
46 friend class rate_limit_queue;
47 friend class https_client;
50 std::unique_lock<std::mutex> lock{ accessMutex, std::defer_lock };
51 std::atomic<milliseconds> sampledTimeInMs{ milliseconds{} };
52 std::atomic<seconds> sRemain{ seconds{} };
53 std::atomic_int64_t getsRemaining{ 1 };
54 std::atomic_bool areWeASpecialBucket{};
55 std::atomic_bool didWeHitRateLimit{};
56 std::atomic_bool doWeWait{};
57 jsonifier::string bucket{};
58 std::mutex accessMutex{};
61 class rate_limit_queue {
63 friend class https_client;
65 inline rate_limit_queue() =
default;
67 inline void initialize() {
68 for (int64_t enumOne =
static_cast<int64_t
>(https_workload_type::Unset); enumOne !=
static_cast<int64_t
>(https_workload_type::Last); enumOne++) {
69 auto tempBucket = jsonifier::toString(std::chrono::duration_cast<nanoseconds>(hrclock::now().time_since_epoch()).count());
70 buckets.emplace(
static_cast<https_workload_type
>(enumOne), tempBucket);
71 rateLimits.emplace(tempBucket, makeUnique<rate_limit_data>())
73 ->second->sampledTimeInMs.store(std::chrono::duration_cast<milliseconds>(sys_clock::now().time_since_epoch()));
74 std::this_thread::sleep_for(1ms);
78 inline rate_limit_data* getEndpointAccess(https_workload_type workloadType) {
79 stop_watch<milliseconds> stopWatch{ milliseconds{ 25000 } };
82 std::chrono::duration_cast<std::chrono::duration<int64_t, std::milli>>(rateLimits[buckets[workloadType]]->sampledTimeInMs.load(std::memory_order_acquire)) +
83 std::chrono::duration_cast<std::chrono::duration<int64_t, std::milli>>(rateLimits[buckets[workloadType]]->sRemain.load(std::memory_order_acquire));
84 if (rateLimits[buckets[workloadType]]->getsRemaining.load(std::memory_order_acquire) <= 0) {
85 auto newNow = std::chrono::duration_cast<std::chrono::duration<int64_t, std::milli>>(sys_clock::now().time_since_epoch());
86 while ((newNow - targetTime).count() <= 0) {
87 if (stopWatch.hasTimeElapsed()) {
90 newNow = std::chrono::duration_cast<std::chrono::duration<int64_t, std::milli>>(sys_clock::now().time_since_epoch());
91 std::this_thread::sleep_for(1us);
95 while (!rateLimits[buckets[workloadType]]->accessMutex.try_lock()) {
96 std::this_thread::sleep_for(1us);
97 if (stopWatch.hasTimeElapsed()) {
101 return rateLimits[buckets[workloadType]].get();
104 inline void releaseEndPointAccess(https_workload_type type) {
105 rateLimits[buckets[type]]->accessMutex.unlock();
109 unordered_map<jsonifier::string, unique_ptr<rate_limit_data>> rateLimits{};
110 unordered_map<https_workload_type, jsonifier::string> buckets{};
The main namespace for the forward-facing interfaces.