Initial revision of animqueue
This commit is contained in:
@@ -1,51 +1,66 @@
|
||||
// The ID allocator.
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This ID allocator attempts to allocate IDs in such a way that the
|
||||
// THE ID ALLOCATOR
|
||||
//
|
||||
// This ID allocator's goal is 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.
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
// 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 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.
|
||||
// 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 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.
|
||||
// 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.
|
||||
//
|
||||
// ID ranges are assigned as follows:
|
||||
// THE NUMERIC RANGES
|
||||
//
|
||||
// 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.
|
||||
// * 0x0000+ : reserved for future expansion.
|
||||
// * 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.
|
||||
//
|
||||
// The operations in this class are:
|
||||
// BATCH REPRESENTATION
|
||||
//
|
||||
// idalloc.initmaster() - reinitialize the ID allocator in master mode.
|
||||
// idalloc.initsynch() - reinitialize the ID allocator in synchronous mode.
|
||||
// idalloc.setqueue_fill() - override the default queue fill level.
|
||||
// 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
|
||||
// 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".
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef IDALLOC_HPP
|
||||
#define IDALLOC_HPP
|
||||
@@ -64,16 +79,44 @@ private:
|
||||
friend int cunittests_idalloc(lua_State *L);
|
||||
|
||||
public:
|
||||
// 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.
|
||||
IdGlobalPool();
|
||||
~IdGlobalPool();
|
||||
|
||||
// Initialize the pool for use in a master model.
|
||||
void init_master(int qf);
|
||||
|
||||
// Initialize the pool for use in a synchronous model.
|
||||
void init_synch(int qf);
|
||||
int64_t get_batch();
|
||||
|
||||
// Create a single unique ID. Ids allocated this way are
|
||||
// unlikely to be predicted correctly.
|
||||
int64_t get_one();
|
||||
void salvage(int64_t batch);
|
||||
|
||||
// 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();
|
||||
|
||||
// When a player pool refills its fifo queue, it refills it
|
||||
// with this many batches.
|
||||
int queue_fill() const { return queue_fill_; }
|
||||
|
||||
// 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);
|
||||
|
||||
// Return the thread's batch to the global pool. If no batch
|
||||
// is present, that's okay. Set the thread's batch to zero (invalid).
|
||||
void salvage_thread(lua_State *L);
|
||||
|
||||
// Allocate an ID for the specified thread. Uses the thread's
|
||||
// batch if possible. If not, fetches one ID from the global pool.
|
||||
int64_t alloc_id_for_thread(lua_State *L);
|
||||
};
|
||||
|
||||
@@ -84,15 +127,34 @@ private:
|
||||
friend int cunittests_idalloc(lua_State *L);
|
||||
|
||||
public:
|
||||
// Construct a player pool.
|
||||
// The Player pool stores a pointer to the global pool.
|
||||
IdPlayerPool(IdGlobalPool *gp);
|
||||
~IdPlayerPool();
|
||||
|
||||
// Refill the fifo queue of batches from the global pool.
|
||||
void refill();
|
||||
|
||||
// Return all batches to the global pool. Leave the fifo empty.
|
||||
void unqueue();
|
||||
|
||||
// Discard all batches. This is only for unit testing.
|
||||
void purge();
|
||||
|
||||
// Get a batch from the fifo. Also refills the fifo.
|
||||
int64_t get_batch();
|
||||
|
||||
// Return the thread's batch to the global pool. If no batch
|
||||
// is present, that's okay. Set the thread's batch to zero (invalid).
|
||||
void salvage_thread(lua_State *L);
|
||||
|
||||
// Fetch a batch from the fifo and install it in the thread.
|
||||
// If the thread already had a batch, the thread's previous batch
|
||||
// is returned to the global pool.
|
||||
void prepare_thread(lua_State *L);
|
||||
|
||||
// Return the size of the queue.
|
||||
int size() { return ranges_.size(); }
|
||||
};
|
||||
|
||||
#endif // IDALLOC_HPP
|
||||
|
||||
Reference in New Issue
Block a user