Rewrite planemap and idalloc in pure C++
This commit is contained in:
@@ -1,237 +1,231 @@
|
||||
#include "luastack.hpp"
|
||||
#include "table.hpp"
|
||||
#include "idalloc.hpp"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
LuaDefine(idalloc_initmaster, "c") {
|
||||
LuaArg allocator, queuefill;
|
||||
LuaVar salvqueue;
|
||||
LuaStack LS(L, allocator, queuefill, salvqueue);
|
||||
LuaDefineType(IdGlobalPool);
|
||||
LuaDefineType(IdPlayerPool);
|
||||
|
||||
// Use the registry by default.
|
||||
if (LS.isnil(allocator)) LS.set(allocator, LuaRegistry);
|
||||
|
||||
LS.checktable(allocator);
|
||||
LS.checknumber(queuefill);
|
||||
LS.call(salvqueue, queue_create);
|
||||
LS.setfield(allocator, "id_salvaged", salvqueue);
|
||||
LS.setfield(allocator, "id_nextbatch", 0x0001000000000000);
|
||||
LS.setfield(allocator, "id_nextid", 0x0010000000000000);
|
||||
LS.setfield(allocator, "id_queuefill", queuefill);
|
||||
return LS.result();
|
||||
static int64_t nthbatch(int64_t n) {
|
||||
return int64_t(0x0001000000000000) + n*256;
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_initsynch, "c") {
|
||||
LuaArg allocator, queuefill;
|
||||
LuaStack LS(L, allocator, queuefill);
|
||||
|
||||
// Use the registry by default.
|
||||
if (LS.isnil(allocator)) LS.set(allocator, LuaRegistry);
|
||||
|
||||
LS.checktable(allocator);
|
||||
LS.checknumber(queuefill);
|
||||
LS.setfield(allocator, "id_salvaged", LuaNil);
|
||||
LS.setfield(allocator, "id_nextbatch", LuaNil);
|
||||
LS.setfield(allocator, "id_nextid", 0x001E000000000000);
|
||||
LS.setfield(allocator, "id_queuefill", queuefill);
|
||||
return LS.result();
|
||||
static bool ranges_equal(const std::deque<int64_t> &dq, int64_t a, int64_t b, int64_t c) {
|
||||
if (dq.size() != 3) return false;
|
||||
if (dq[0] != a) return false;
|
||||
if (dq[1] != b) return false;
|
||||
if (dq[2] != c) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_refill, "c") {
|
||||
LuaArg allocator, queue;
|
||||
LuaVar salvaged, batch;
|
||||
lua_Integer qhead, qtail, shead, stail, nextb, queuefill;
|
||||
LuaStack LS(L, allocator, queue, salvaged, batch);
|
||||
IdGlobalPool::IdGlobalPool() {
|
||||
salvaged_.clear();
|
||||
next_batch_ = 0;
|
||||
next_id_ = 0;
|
||||
queue_fill_ = 10;
|
||||
}
|
||||
|
||||
// Use the registry by default.
|
||||
if (LS.isnil(allocator)) LS.set(allocator, LuaRegistry);
|
||||
IdGlobalPool::~IdGlobalPool() {
|
||||
}
|
||||
|
||||
// Get salvaged batch table. If there is none, we're in donotpredict mode.
|
||||
LS.getfield(salvaged, allocator, "id_salvaged");
|
||||
if (LS.isnil(salvaged)) {
|
||||
return LS.result();
|
||||
}
|
||||
void IdGlobalPool::init_master(int qf) {
|
||||
salvaged_.clear();
|
||||
next_batch_ = 0x0001000000000000;
|
||||
next_id_ = 0x0010000000000000;
|
||||
queue_fill_ = qf;
|
||||
}
|
||||
|
||||
// Get the head and tail of the queue.
|
||||
LS.checktable(queue);
|
||||
LS.getfield(qhead, queue, "head");
|
||||
LS.getfield(qtail, queue, "tail");
|
||||
lua_Integer oqhead = qhead;
|
||||
void IdGlobalPool::init_synch(int qf) {
|
||||
salvaged_.clear();
|
||||
next_batch_ = 0;
|
||||
next_id_ = 0x001E000000000000;
|
||||
queue_fill_ = qf;
|
||||
}
|
||||
|
||||
// Try using salvaged batches.
|
||||
LS.getfield(queuefill, allocator, "id_queuefill");
|
||||
if (qhead - qtail < queuefill) {
|
||||
// Grab the head and tail of the salvaged batches.
|
||||
LS.checktable(salvaged);
|
||||
LS.getfield(shead, salvaged, "head");
|
||||
LS.getfield(stail, salvaged, "tail");
|
||||
int64_t IdGlobalPool::get_one() {
|
||||
return next_id_++;
|
||||
}
|
||||
|
||||
// Get salvaged batches where possible.
|
||||
while ((stail < shead) && (qhead - qtail < queuefill)) {
|
||||
LS.rawget(batch, salvaged, stail);
|
||||
LS.rawset(salvaged, stail, LuaNil);
|
||||
stail += 1;
|
||||
LS.checknumber(batch);
|
||||
LS.rawset(queue, qhead, batch);
|
||||
qhead += 1;
|
||||
int64_t IdGlobalPool::get_batch() {
|
||||
int64_t batch;
|
||||
if (salvaged_.empty()) {
|
||||
if (next_batch_ == 0) {
|
||||
batch = 0;
|
||||
} else {
|
||||
batch = next_batch_;
|
||||
next_batch_ += 256;
|
||||
}
|
||||
|
||||
// Update the head and tail of the salvaged batches.
|
||||
LS.setfield(salvaged, "head", shead);
|
||||
LS.setfield(salvaged, "tail", stail);
|
||||
}
|
||||
|
||||
// Try using newly-created batches.
|
||||
if (qhead - qtail < queuefill) {
|
||||
// Grab the next batch counter.
|
||||
LS.getfield(nextb, allocator, "id_nextbatch");
|
||||
|
||||
// Get newly-allocated batches to fill the rest.
|
||||
while (qhead - qtail < queuefill) {
|
||||
LS.rawset(queue, qhead, nextb);
|
||||
nextb += 256;
|
||||
qhead += 1;
|
||||
}
|
||||
|
||||
// Update the counter in the registry.
|
||||
LS.setfield(allocator, "id_nextbatch", nextb);
|
||||
}
|
||||
|
||||
// Update the head of the queue.
|
||||
if (oqhead != qhead) {
|
||||
LS.setfield(queue, "head", qhead);
|
||||
}
|
||||
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_unqueue, "c") {
|
||||
LuaArg allocator, queue;
|
||||
LuaVar salvaged, batch;
|
||||
lua_Integer qhead, qtail, shead, stail;
|
||||
LuaStack LS(L, allocator, queue, salvaged, batch);
|
||||
|
||||
// Use the registry by default.
|
||||
if (LS.isnil(allocator)) LS.set(allocator, LuaRegistry);
|
||||
|
||||
// Get the head and tail of the queue.
|
||||
LS.checktable(queue);
|
||||
LS.getfield(qhead, queue, "head");
|
||||
LS.getfield(qtail, queue, "tail");
|
||||
|
||||
// Grab the table of salvaged batches.
|
||||
// If there is none, we're in donotpredict mode. In that
|
||||
// case, just empty the queue and dump the batches.
|
||||
LS.getfield(salvaged, allocator, "id_salvaged");
|
||||
if (LS.isnil(salvaged)) {
|
||||
while (qhead > qtail) {
|
||||
LS.rawset(queue, qtail, LuaNil);
|
||||
qtail += 1;
|
||||
}
|
||||
LS.setfield(queue, "tail", qtail);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
// We're in master mode. Transfer batches from the queue
|
||||
// into the salvaged batches table.
|
||||
LS.checktable(salvaged);
|
||||
LS.getfield(shead, salvaged, "head");
|
||||
LS.getfield(stail, salvaged, "tail");
|
||||
|
||||
while (qhead > qtail) {
|
||||
LS.rawget(batch, queue, qtail);
|
||||
LS.rawset(queue, qtail, LuaNil);
|
||||
qtail += 1;
|
||||
|
||||
LS.checknumber(batch);
|
||||
LS.rawset(salvaged, shead, batch);
|
||||
shead += 1;
|
||||
}
|
||||
|
||||
// Update the queue pointers.
|
||||
LS.setfield(queue, "tail", qtail);
|
||||
LS.setfield(salvaged, "head", shead);
|
||||
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_preparethread, "c") {
|
||||
LuaArg queue, thread;
|
||||
LuaVar batch;
|
||||
LuaStack LS(L, queue, thread, batch);
|
||||
|
||||
// Get the thread.
|
||||
lua_State *TH = LS.ckthread(thread);
|
||||
|
||||
// Pop a batch from the queue. If there's nothing in
|
||||
// the queue, just leave the thread unprepped.
|
||||
LS.call(batch, queue_pop, queue);
|
||||
if (LS.isnil(batch)) {
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
// Store the batch into the thread.
|
||||
lua_setnextid(TH, LS.ckinteger(batch));
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_salvagethread, "c") {
|
||||
LuaArg allocator, thread;
|
||||
LuaVar salvaged;
|
||||
LuaStack LS(L, allocator, thread, salvaged);
|
||||
|
||||
lua_State *TH = LS.ckthread(thread);
|
||||
lua_Integer idbatch = lua_getnextid(TH);
|
||||
lua_setnextid(TH, 0);
|
||||
if (idbatch == 0) {
|
||||
return LS.result();
|
||||
}
|
||||
if ((idbatch & 0xFF) >= 128) {
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
// Use the registry by default.
|
||||
if (LS.isnil(allocator)) LS.set(allocator, LuaRegistry);
|
||||
|
||||
// Push the batch onto the queue of salvaged batches.
|
||||
// If the table of salvaged batches is nil, we're in donotpredict
|
||||
// mode. In that case, don't bother salvaging.
|
||||
LS.getfield(salvaged, allocator, "id_salvaged");
|
||||
if (LS.isnil(salvaged)) {
|
||||
return LS.result();
|
||||
}
|
||||
LS.call(queue_push, salvaged, idbatch);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_allocid, "c") {
|
||||
LuaArg allocator;
|
||||
LuaRet result;
|
||||
LuaStack LS(L, allocator, result);
|
||||
|
||||
lua_Integer id = lua_getnextid(L);
|
||||
if (id == 0) {
|
||||
// Use the registry by default.
|
||||
if (LS.isnil(allocator)) LS.set(allocator, LuaRegistry);
|
||||
LS.getfield(id, allocator, "id_nextid");
|
||||
LS.setfield(allocator, "id_nextid", id + 1);
|
||||
} else {
|
||||
lua_Integer next = id + 1;
|
||||
if ((next & 0xFF) == 0) {
|
||||
next = 0;
|
||||
}
|
||||
lua_setnextid(L, next);
|
||||
batch = salvaged_.back();
|
||||
salvaged_.pop_back();
|
||||
}
|
||||
LS.set(result, id);
|
||||
return LS.result();
|
||||
return batch;
|
||||
}
|
||||
|
||||
LuaDefine(idalloc_getthreadbatch, "c") {
|
||||
LuaArg thread;
|
||||
LuaRet result;
|
||||
LuaStack LS(L, thread, result);
|
||||
|
||||
lua_State *TH = LS.ckthread(thread);
|
||||
lua_Integer id = lua_getnextid(TH);
|
||||
LS.set(result, id);
|
||||
return LS.result();
|
||||
void IdGlobalPool::salvage(int64_t batch) {
|
||||
if (batch == 0) return;
|
||||
if (next_batch_ == 0) return;
|
||||
if ((batch & 0xFF) >= 128) return;
|
||||
salvaged_.push_back(batch);
|
||||
}
|
||||
|
||||
void IdGlobalPool::salvage_thread(lua_State *L) {
|
||||
salvage(lua_getnextid(L));
|
||||
lua_setnextid(L, 0);
|
||||
}
|
||||
|
||||
int64_t IdGlobalPool::alloc_id_for_thread(lua_State *L) {
|
||||
int64_t batch = lua_getnextid(L);
|
||||
if (batch != 0) {
|
||||
int64_t id = batch;
|
||||
batch += 1;
|
||||
if ((batch & 0xFF) == 0) batch = 0;
|
||||
lua_setnextid(L, batch);
|
||||
return id;
|
||||
} else {
|
||||
return get_one();
|
||||
}
|
||||
}
|
||||
|
||||
IdPlayerPool::IdPlayerPool(IdGlobalPool *gp) {
|
||||
global_ = gp;
|
||||
}
|
||||
|
||||
IdPlayerPool::~IdPlayerPool() {
|
||||
}
|
||||
|
||||
void IdPlayerPool::purge() {
|
||||
ranges_.clear();
|
||||
}
|
||||
|
||||
void IdPlayerPool::refill() {
|
||||
while (int(ranges_.size()) < global_->queue_fill()) {
|
||||
ranges_.push_back(global_->get_batch());
|
||||
}
|
||||
}
|
||||
|
||||
void IdPlayerPool::unqueue() {
|
||||
while (!ranges_.empty()) {
|
||||
global_->salvage(ranges_.front());
|
||||
ranges_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t IdPlayerPool::get_batch() {
|
||||
while (int(ranges_.size()) < global_->queue_fill() + 1) {
|
||||
ranges_.push_back(global_->get_batch());
|
||||
}
|
||||
int64_t batch = ranges_.front();
|
||||
ranges_.pop_front();
|
||||
return batch;
|
||||
}
|
||||
|
||||
void IdPlayerPool::salvage_thread(lua_State *L) {
|
||||
global_->salvage_thread(L);
|
||||
}
|
||||
|
||||
void IdPlayerPool::prepare_thread(lua_State *L) {
|
||||
global_->salvage_thread(L);
|
||||
lua_setnextid(L, get_batch());
|
||||
}
|
||||
|
||||
LuaDefine(cunittests_idalloc, "c") {
|
||||
IdGlobalPool gp;
|
||||
IdPlayerPool pp(&gp);
|
||||
|
||||
// Synchronous pools produce IDs starting at 0x001E000000000000
|
||||
gp.init_synch(3);
|
||||
assert(gp.get_one() == 0x001E000000000000);
|
||||
assert(gp.get_one() == 0x001E000000000001);
|
||||
assert(gp.get_one() == 0x001E000000000002);
|
||||
|
||||
// Master pools produce IDs starting at 0x0010000000000000
|
||||
gp.init_master(3);
|
||||
assert(gp.get_one() == 0x0010000000000000);
|
||||
assert(gp.get_one() == 0x0010000000000001);
|
||||
assert(gp.get_one() == 0x0010000000000002);
|
||||
|
||||
// Synchronous pools produce only null batches.
|
||||
gp.init_synch(3);
|
||||
assert(gp.get_batch() == 0);
|
||||
assert(gp.get_batch() == 0);
|
||||
gp.salvage(nthbatch(5));
|
||||
assert(gp.get_batch() == 0);
|
||||
|
||||
// Simple fetch batches with a few salvages.
|
||||
gp.init_master(3);
|
||||
assert(gp.get_batch() == nthbatch(0));
|
||||
assert(gp.get_batch() == nthbatch(1));
|
||||
assert(gp.get_batch() == nthbatch(2));
|
||||
gp.salvage(nthbatch(182));
|
||||
gp.salvage(nthbatch(183));
|
||||
assert(gp.get_batch() == nthbatch(183));
|
||||
assert(gp.get_batch() == nthbatch(182));
|
||||
assert(gp.get_batch() == nthbatch(3));
|
||||
|
||||
// Salvage of a zero-batch does nothing.
|
||||
gp.init_master(3);
|
||||
assert(gp.get_batch() == nthbatch(0));
|
||||
assert(gp.get_batch() == nthbatch(1));
|
||||
gp.salvage(0);
|
||||
assert(gp.get_batch() == nthbatch(2));
|
||||
|
||||
// Salvage of a partial batch.
|
||||
gp.init_master(3);
|
||||
assert(gp.get_batch() == nthbatch(0));
|
||||
assert(gp.get_batch() == nthbatch(1));
|
||||
gp.salvage(nthbatch(142) + 10);
|
||||
assert(gp.get_batch() == nthbatch(142) + 10);
|
||||
assert(gp.get_batch() == nthbatch(2));
|
||||
|
||||
// Salvage of a half-empty batch does nothing.
|
||||
gp.init_master(3);
|
||||
assert(gp.get_batch() == nthbatch(0));
|
||||
assert(gp.get_batch() == nthbatch(1));
|
||||
gp.salvage(nthbatch(142) + 145);
|
||||
assert(gp.get_batch() == nthbatch(2));
|
||||
|
||||
// Test refill from master.
|
||||
pp.purge();
|
||||
gp.init_master(3);
|
||||
pp.refill();
|
||||
assert(ranges_equal(pp.ranges_, nthbatch(0), nthbatch(1), nthbatch(2)));
|
||||
|
||||
// Now test that get_batch keeps the pool filled from master.
|
||||
assert(pp.get_batch() == nthbatch(0));
|
||||
assert(ranges_equal(pp.ranges_, nthbatch(1), nthbatch(2), nthbatch(3)));
|
||||
|
||||
// Test unqueueing the batches.
|
||||
assert(gp.get_batch() == nthbatch(4));
|
||||
assert(gp.get_batch() == nthbatch(5));
|
||||
pp.unqueue();
|
||||
assert(gp.get_batch() == nthbatch(3));
|
||||
assert(gp.get_batch() == nthbatch(2));
|
||||
assert(gp.get_batch() == nthbatch(1));
|
||||
assert(gp.get_batch() == nthbatch(6));
|
||||
|
||||
// Try preparing a thread and salvaging a thread.
|
||||
pp.purge();
|
||||
gp.init_master(3);
|
||||
lua_setnextid(L, 0);
|
||||
pp.prepare_thread(L);
|
||||
assert(lua_getnextid(L) == nthbatch(0));
|
||||
lua_setnextid(L, 0);
|
||||
pp.prepare_thread(L);
|
||||
assert(lua_getnextid(L) == nthbatch(1));
|
||||
|
||||
// Try salvaging the pool from the thread.
|
||||
pp.salvage_thread(L);
|
||||
assert(lua_getnextid(L) == 0);
|
||||
assert(gp.get_batch() == nthbatch(1));
|
||||
|
||||
// Allocate IDs from inside a thread.
|
||||
lua_setnextid(L, 0xFD);
|
||||
gp.init_master(3);
|
||||
assert(gp.alloc_id_for_thread(L) == 0xFD);
|
||||
assert(gp.alloc_id_for_thread(L) == 0xFE);
|
||||
assert(gp.alloc_id_for_thread(L) == 0xFF);
|
||||
assert(gp.alloc_id_for_thread(L) == 0x0010000000000000);
|
||||
assert(lua_getnextid(L) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user