2021-01-02 13:31:18 -05:00
|
|
|
// The ID allocator.
|
|
|
|
|
//
|
|
|
|
|
// This ID allocator attempts to allocate IDs in such a way that the
|
|
|
|
|
// synchronous model gets the same IDs as the master model.
|
|
|
|
|
//
|
|
|
|
|
// Every logged-in player maintains a "batch queue". That's a fifo queue
|
|
|
|
|
// of ID batches. An ID batch is a contiguous range of IDs, containing
|
|
|
|
|
// between 128 and 256 contiguous IDs. The batch queue is difference
|
|
|
|
|
// transmitted, to ensure that the synchronous model has the same
|
|
|
|
|
// batches as the master model.
|
|
|
|
|
//
|
|
|
|
|
// When a player creates a thread, that thread gets an ID batch from the
|
|
|
|
|
// player's batch queue. When that thread allocates IDs,
|
|
|
|
|
// it uses the batch it was allocated. If the batch is used up, then the
|
|
|
|
|
// thread falls back to using a global fallback allocator. Such fallback IDs
|
|
|
|
|
// are not likely to be predicted correctly.
|
|
|
|
|
//
|
|
|
|
|
// When a player creates a thread, he uses up one of his batches. In the
|
|
|
|
|
// master model, the batch queue is 'refilled' by creating a new batch.
|
|
|
|
|
// In the synchronous model, the batch queue 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 0, 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 master model will reclaim that batch,
|
|
|
|
|
// putting it into a global salvage pool. The salvage batches are later
|
|
|
|
|
// used when refilling batch queues.
|
|
|
|
|
//
|
|
|
|
|
// ID ranges are assigned as follows:
|
|
|
|
|
//
|
|
|
|
|
// 0x0000+ : reserved for future expansion.
|
|
|
|
|
// 0x0001+ : used by master model to allocate batches.
|
|
|
|
|
// 0x0010+ : used by master model to allocate unpredictable IDs.
|
|
|
|
|
// 0x001E+ : used by synchronous model to allocate stopgap IDs.
|
|
|
|
|
//
|
|
|
|
|
// The operations in this class are:
|
|
|
|
|
//
|
|
|
|
|
// idalloc.initmaster() - reinitialize the ID allocator in master mode.
|
|
|
|
|
// idalloc.initsynch() - reinitialize the ID allocator in synchronous mode.
|
2021-01-06 15:10:21 -05:00
|
|
|
// idalloc.setqueue_fill() - override the default queue fill level.
|
2021-01-02 13:31:18 -05:00
|
|
|
// idalloc.refill(bq) - get batches from the global pool until the batch queue is full.
|
|
|
|
|
// idalloc.unqueue(bq) - push batches from the batch queue back into the global pool.
|
|
|
|
|
// idalloc.preparethread(bq, co) - transfer a batch from the batch queue to the coroutine
|
|
|
|
|
// idalloc.salvagethread(co) - push any batch in the dead coroutine back into the global pool
|
|
|
|
|
//
|
|
|
|
|
// idalloc.allocid() - get an ID using either the current thread's pool or the global pool
|
2020-12-05 18:57:53 -05:00
|
|
|
|
|
|
|
|
#ifndef IDALLOC_HPP
|
|
|
|
|
#define IDALLOC_HPP
|
|
|
|
|
|
2021-01-06 15:10:21 -05:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <deque>
|
2020-12-05 18:57:53 -05:00
|
|
|
#include "luastack.hpp"
|
|
|
|
|
|
2021-01-06 15:10:21 -05:00
|
|
|
class IdGlobalPool {
|
|
|
|
|
private:
|
|
|
|
|
std::vector<int64_t> salvaged_;
|
|
|
|
|
int64_t next_batch_;
|
|
|
|
|
int64_t next_id_;
|
|
|
|
|
int queue_fill_;
|
|
|
|
|
friend int cunittests_idalloc(lua_State *L);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
IdGlobalPool();
|
|
|
|
|
~IdGlobalPool();
|
|
|
|
|
|
|
|
|
|
void init_master(int qf);
|
|
|
|
|
void init_synch(int qf);
|
|
|
|
|
int64_t get_batch();
|
|
|
|
|
int64_t get_one();
|
|
|
|
|
void salvage(int64_t batch);
|
|
|
|
|
int queue_fill() const { return queue_fill_; }
|
|
|
|
|
void salvage_thread(lua_State *L);
|
|
|
|
|
int64_t alloc_id_for_thread(lua_State *L);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class IdPlayerPool {
|
|
|
|
|
private:
|
|
|
|
|
IdGlobalPool *global_;
|
|
|
|
|
std::deque<int64_t> ranges_;
|
|
|
|
|
friend int cunittests_idalloc(lua_State *L);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
IdPlayerPool(IdGlobalPool *gp);
|
|
|
|
|
~IdPlayerPool();
|
|
|
|
|
|
|
|
|
|
void refill();
|
|
|
|
|
void unqueue();
|
|
|
|
|
void purge();
|
|
|
|
|
int64_t get_batch();
|
|
|
|
|
void salvage_thread(lua_State *L);
|
|
|
|
|
void prepare_thread(lua_State *L);
|
|
|
|
|
};
|
2020-12-05 18:57:53 -05:00
|
|
|
|
|
|
|
|
#endif // IDALLOC_HPP
|
|
|
|
|
|