2021-01-12 14:14:38 -05:00
|
|
|
///////////////////////////////////////////////////////////////////
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2021-01-12 14:14:38 -05:00
|
|
|
// THE ID ALLOCATOR
|
|
|
|
|
//
|
|
|
|
|
// This ID allocator's goal is to allocate IDs in such a way that the
|
2021-01-02 13:31:18 -05:00
|
|
|
// synchronous model gets the same IDs as the master model.
|
|
|
|
|
//
|
2021-01-12 14:14:38 -05:00
|
|
|
// DESIGN PRINCIPLES
|
|
|
|
|
//
|
|
|
|
|
// There are two classes defined here: IdGlobalPool, and IdPlayerPool.
|
|
|
|
|
//
|
|
|
|
|
// Every logged-in player maintains an IdPlayerPool. That's basically a fifo
|
|
|
|
|
// queue of ID batches. An ID batch is a contiguous range of IDs, containing
|
|
|
|
|
// between 128 and 256 contiguous IDs. The IdPlayerPool is difference
|
|
|
|
|
// transmitted, to ensure that the synchronous model has the same batches as the
|
|
|
|
|
// master model.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
// When a player creates a thread, that thread gets an ID batch from the
|
2021-01-12 14:14:38 -05:00
|
|
|
// IdPlayerPool. To make this possible, the Lua runtime has been modified so
|
|
|
|
|
// that a thread can contain an ID batch. When that thread allocates IDs, it
|
|
|
|
|
// uses the batch it was allocated. In the unlikely event that a thread's batch
|
|
|
|
|
// is used up, the thread falls back to using the IdGlobalPool. Such fallback
|
|
|
|
|
// IDs are not likely to be predicted correctly.
|
|
|
|
|
//
|
|
|
|
|
// When a player creates a thread, he uses up one batch from his IdPlayerPool.
|
|
|
|
|
// In the master model, the player pool is 'refilled' by asking the IdGlobalPool
|
|
|
|
|
// to create a new batch. In the synchronous model, the IdPlayerPool is not
|
|
|
|
|
// directly refilled, but the difference transmitter effectively refills it. It
|
|
|
|
|
// is imperative that this difference transmission happen before the player's
|
|
|
|
|
// asynchronous model runs out of batches, otherwise we'll get prediction
|
|
|
|
|
// failures.
|
|
|
|
|
//
|
|
|
|
|
// It is common that a thread will only use 1 or 2 IDs. If a thread exits
|
|
|
|
|
// without using up most of its IDs, then the batch it contains is still a
|
|
|
|
|
// pretty usable batch. The batch gets returned to the IdGlobalPool, which will
|
|
|
|
|
// eventually use that salvaged batch to satisfy an IdPlayerPool refill request.
|
|
|
|
|
//
|
|
|
|
|
// THE NUMERIC RANGES
|
|
|
|
|
//
|
2021-08-03 11:25:12 -04:00
|
|
|
// * 0x0000+ : reserved for manually-created tangible IDs.
|
2021-01-12 14:14:38 -05:00
|
|
|
// * 0x0001+ : used by master model's IdGlobalPool to create batches.
|
|
|
|
|
// * 0x0010+ : used by master model's IdGlobalPool to create individual IDs.
|
|
|
|
|
// * 0x001E+ : used by sync model's IdGlobalPool to create individual IDs.
|
|
|
|
|
//
|
|
|
|
|
// BATCH REPRESENTATION
|
|
|
|
|
//
|
|
|
|
|
// A batch is represented as a 64-bit integer. The batch contains all IDs
|
|
|
|
|
// starting with that integer, and incrementing, until the the two lowest digits
|
|
|
|
|
// are 0x00.
|
|
|
|
|
//
|
|
|
|
|
// For example, consider the batch ID 0x11111111111111FC. That batch includes
|
|
|
|
|
// these IDs:
|
|
|
|
|
//
|
|
|
|
|
// * 0x11111111111111FC
|
|
|
|
|
// * 0x11111111111111FD
|
|
|
|
|
// * 0x11111111111111FE
|
|
|
|
|
// * 0x11111111111111FF
|
|
|
|
|
//
|
|
|
|
|
// But it does not include 0x1111111111111200.
|
|
|
|
|
//
|
|
|
|
|
// As a special case, the number 0 is used to indicate "invalid batch".
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
2020-12-05 18:57:53 -05:00
|
|
|
|
|
|
|
|
#ifndef IDALLOC_HPP
|
|
|
|
|
#define IDALLOC_HPP
|
|
|
|
|
|
2022-02-23 23:08:28 -05:00
|
|
|
#include "wrap-map.hpp"
|
|
|
|
|
#include "wrap-sstream.hpp"
|
|
|
|
|
#include "wrap-deque.hpp"
|
2020-12-05 18:57:53 -05:00
|
|
|
#include "luastack.hpp"
|
2021-03-05 15:27:58 -05:00
|
|
|
#include "streambuffer.hpp"
|
2021-11-21 13:35:39 -05:00
|
|
|
#include "debugcollector.hpp"
|
2020-12-05 18:57:53 -05:00
|
|
|
|
2022-02-23 23:08:28 -05:00
|
|
|
#include <cstdint>
|
2022-02-25 19:57:23 -05:00
|
|
|
#include <ostream>
|
2022-02-23 23:08:28 -05:00
|
|
|
|
2021-01-06 15:10:21 -05:00
|
|
|
class IdGlobalPool {
|
|
|
|
|
public:
|
2021-01-12 14:14:38 -05:00
|
|
|
// Construct and destroy global pools. Note that after constructing
|
|
|
|
|
// a global pool, it is generally also necessary to initialize it
|
|
|
|
|
// for Master or Synchronous operation using init_master or init_synch.
|
|
|
|
|
// Any attempt to use a pool that hasn't been init_master or init_synch
|
|
|
|
|
// will fail.
|
2021-01-06 15:10:21 -05:00
|
|
|
IdGlobalPool();
|
|
|
|
|
~IdGlobalPool();
|
|
|
|
|
|
2021-01-12 14:14:38 -05:00
|
|
|
// Initialize the pool for use in a master model.
|
2021-07-18 17:48:39 -04:00
|
|
|
void init_master();
|
2021-01-12 14:14:38 -05:00
|
|
|
|
|
|
|
|
// Initialize the pool for use in a synchronous model.
|
2021-07-18 17:48:39 -04:00
|
|
|
void init_synch();
|
2021-01-12 14:14:38 -05:00
|
|
|
|
|
|
|
|
// Create a single unique ID. Ids allocated this way are
|
|
|
|
|
// unlikely to be predicted correctly.
|
2021-01-06 15:10:21 -05:00
|
|
|
int64_t get_one();
|
2021-01-12 14:14:38 -05:00
|
|
|
|
|
|
|
|
// Obtain a batch of IDs from the global pool. In a master
|
|
|
|
|
// model, the batch is guaranteed to contain at least 128 IDs.
|
|
|
|
|
// In a synchronous model, the batch is always zero (invalid).
|
|
|
|
|
int64_t get_batch();
|
|
|
|
|
|
|
|
|
|
// Try to return the specified batch to the global pool.
|
|
|
|
|
// The salvage operation quietly does nothing if the batch is
|
|
|
|
|
// zero, or the batch contains fewer than 128 IDs.
|
|
|
|
|
void salvage(int64_t batch);
|
|
|
|
|
|
2021-03-13 13:05:00 -05:00
|
|
|
// Serialize to or deserialize from a streambuffer.
|
2021-07-26 13:12:44 -04:00
|
|
|
void serialize(StreamBuffer *sb) const;
|
2021-03-05 15:27:58 -05:00
|
|
|
void deserialize(StreamBuffer *sb);
|
2021-01-06 15:10:21 -05:00
|
|
|
|
2021-07-21 16:10:29 -04:00
|
|
|
// Generate a debug string.
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string debug_string() const;
|
2021-07-21 16:10:29 -04:00
|
|
|
|
2021-01-06 15:10:21 -05:00
|
|
|
private:
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::vector<int64_t> salvaged_;
|
2021-07-21 16:10:29 -04:00
|
|
|
int64_t next_batch_;
|
|
|
|
|
int64_t next_id_;
|
2021-01-12 15:49:05 -05:00
|
|
|
friend int unittests_idalloc(lua_State *L);
|
2021-07-21 16:10:29 -04:00
|
|
|
};
|
2021-01-06 15:10:21 -05:00
|
|
|
|
2021-07-21 16:10:29 -04:00
|
|
|
class IdPlayerPool {
|
2021-01-06 15:10:21 -05:00
|
|
|
public:
|
2021-01-12 14:14:38 -05:00
|
|
|
// Construct a player pool.
|
2021-03-14 18:17:34 -04:00
|
|
|
//
|
|
|
|
|
// The fifo is initially in the disabled state. In the disabled state, the
|
|
|
|
|
// fifo is not kept filled. You can still use the allocator when the fifo is
|
|
|
|
|
// disabled - doing so just fetches a batch from the global pool.
|
|
|
|
|
//
|
|
|
|
|
IdPlayerPool(IdGlobalPool *g);
|
2021-01-06 15:10:21 -05:00
|
|
|
~IdPlayerPool();
|
|
|
|
|
|
2021-07-18 17:48:39 -04:00
|
|
|
// Set the fifo capacity. Max=255.
|
|
|
|
|
void set_fifo_capacity(int n);
|
|
|
|
|
int get_fifo_capacity() const { return fifo_capacity_; }
|
2021-01-12 14:14:38 -05:00
|
|
|
|
2021-03-14 18:17:34 -04:00
|
|
|
// Return all batches from the fifo to the global pool.
|
2021-01-06 15:10:21 -05:00
|
|
|
void unqueue();
|
2021-01-12 14:14:38 -05:00
|
|
|
|
2021-03-14 18:17:34 -04:00
|
|
|
// Refill the fifo of batches from the global pool.
|
|
|
|
|
void refill();
|
|
|
|
|
|
2021-10-14 11:43:16 -04:00
|
|
|
// Get a single ID from the fifo. Also refills the fifo.
|
|
|
|
|
int64_t get_one();
|
2021-01-12 14:14:38 -05:00
|
|
|
|
|
|
|
|
// Return the size of the queue.
|
|
|
|
|
int size() { return ranges_.size(); }
|
2021-03-05 15:27:58 -05:00
|
|
|
|
2021-07-13 16:34:24 -04:00
|
|
|
// Return true if the two pools are identical.
|
|
|
|
|
bool exactly_equal(const IdPlayerPool &other) const;
|
|
|
|
|
|
|
|
|
|
// Check that the pool is valid.
|
|
|
|
|
bool valid() const;
|
|
|
|
|
|
|
|
|
|
// unit testing functions.
|
|
|
|
|
//
|
|
|
|
|
// These let you manipulate the deque explicitly. But that's
|
|
|
|
|
// only useful for unit testing.
|
|
|
|
|
//
|
|
|
|
|
void test_push_back(int64_t range);
|
|
|
|
|
void test_pop_front();
|
|
|
|
|
void test_clear_ranges();
|
|
|
|
|
|
2021-03-13 13:05:00 -05:00
|
|
|
// Serialize to or deserialize from a streambuffer.
|
|
|
|
|
// Caution: the pointer to the global pool is not serialized or deserialized.
|
2021-07-26 13:12:44 -04:00
|
|
|
void serialize(StreamBuffer *sb) const;
|
2021-03-05 15:27:58 -05:00
|
|
|
void deserialize(StreamBuffer *sb);
|
2021-07-13 16:34:24 -04:00
|
|
|
|
|
|
|
|
// Difference transmission
|
|
|
|
|
//
|
2021-07-25 19:22:39 -04:00
|
|
|
void diff(const IdPlayerPool &auth, StreamBuffer *sb) const;
|
2021-11-21 13:35:39 -05:00
|
|
|
void patch(StreamBuffer *sb, DebugCollector *dbc);
|
2021-07-21 16:10:29 -04:00
|
|
|
|
|
|
|
|
// Debug string.
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string debug_string() const;
|
2021-07-25 19:22:39 -04:00
|
|
|
|
2021-07-21 16:10:29 -04:00
|
|
|
private:
|
|
|
|
|
IdGlobalPool *global_;
|
|
|
|
|
int fifo_capacity_;
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::deque<int64_t> ranges_;
|
2021-09-07 17:37:23 -04:00
|
|
|
friend int lfn_unittests_idalloc(lua_State *L);
|
2021-01-06 15:10:21 -05:00
|
|
|
};
|
2020-12-05 18:57:53 -05:00
|
|
|
|
|
|
|
|
#endif // IDALLOC_HPP
|
|
|
|
|
|