Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 3 additions & 18 deletions Source/src/peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4881,25 +4881,10 @@ uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void)


#if defined(_WIN32)
// No timer-jitter harvesting: it slept eight times, and a Windows Sleep(1) lasts a whole timer
// tick, so every peer took ~125 ms to construct. The OS entropy source and the counter below
// are what make the value unique, as on every other platform.
uint64_t g= MafiaNet::GetTimeUS();

MafiaNet::TimeUS lastTime, thisTime;
int j;
// Sleep a small random time, then use the last 4 bits as a source of randomness
for (j=0; j < 8; j++)
{
lastTime = MafiaNet::GetTimeUS();
RakSleep(1);
RakSleep(0);
thisTime = MafiaNet::GetTimeUS();
MafiaNet::TimeUS diff = thisTime-lastTime;
unsigned int diff4Bits = (unsigned int) (diff & 15);
diff4Bits <<= 32-4;
diff4Bits >>= j*4;
((char*)&g)[j] ^= diff4Bits;
}
// Timer jitter alone collides when peers are created back to back, and is
// constant under a test's virtual clock. Mix in the same entropy as below.
static std::atomic<uint64_t> sequence {0};
std::random_device entropy;
g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy();
Expand Down
33 changes: 33 additions & 0 deletions Tests/Integration/PeerGuidTimingTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2026, MafiaHub
*
* This source code is licensed under the MIT-style license found in the
* license.txt file in the root directory of this source tree.
*/

// Generating a GUID must not sleep. Every RakPeer generates one when it is constructed,
// so a sleeping generator is paid by every peer: on Windows a Sleep(1) lasts a whole timer
// tick (~15.6 ms), and eight of them made each peer cost ~125 ms, which was most of the
// unit suite's run time there. This reads the wall clock, so it lives in the integration
// suite.

#include <gtest/gtest.h>

#include "mafianet/get_time.h"
#include "mafianet/peer_interface.h"

using namespace MafiaNet;

TEST(PeerGuidTiming, GeneratingGuidsDoesNotSleep)
{
// A sleep costs at least a millisecond even at the finest Windows timer resolution
// (another process may have raised it from the default 15.6 ms), so 256 calls that
// each slept once could not finish in 100 ms. Without sleeping they take microseconds.
const int kGuids = 256;
const TimeMS start = GetTimeMS();
for (int i = 0; i < kGuids; ++i)
(void) RakPeerInterface::Get64BitUniqueRandomNumber();
const TimeMS elapsed = GetTimeMS() - start;

EXPECT_LT(elapsed, 100u) << kGuids << " GUIDs took " << elapsed << " ms";
}
18 changes: 18 additions & 0 deletions Tests/Unit/PeerGuidTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

#include "mafianet/types.h"
#include "mafianet/bit_stream.h"
#include "mafianet/peer_interface.h"

#include <set>
#include <type_traits>
#include <string.h>

Expand Down Expand Up @@ -80,3 +82,19 @@ TEST(PeerGuid, SerializesByteIdenticallyToUint64)
<< "PeerGuid did not round-trip through a BitStream for 0x" << std::hex << v;
}
}

// Every peer's GUID comes from Get64BitUniqueRandomNumber. Peers created back to back in
// one process -- a server and its clients in a test, or several peers in one tool -- must
// still get distinct values, and neither 0 ("not generated") nor all-ones (the unassigned
// sentinel) may ever come out.
TEST(PeerGuid, UniqueRandomNumbersAreDistinctBackToBack)
{
std::set<uint64_t> seen;
for (int i = 0; i < 10000; ++i)
{
const uint64_t g = RakPeerInterface::Get64BitUniqueRandomNumber();
ASSERT_NE(0u, g);
ASSERT_NE(UNASSIGNED_RAKNET_GUID.g, g);
ASSERT_TRUE(seen.insert(g).second) << "repeated after " << i << " calls";
}
}
Loading