60 Deprecated AsyncObjectLoader
Status: This chapter records a deprecated ROT design and is retained for historical reference.
Threads used for async I/O
Either the PersistStore has a thread pool for I/O or each PSpace has a dedicated I/O thread. To help allow PSpaces to be lightweight, we will assume the PersistStore has a thread pool. Possibly one thread is enough.
IPersistable objects in the LSS begin with their size in bytes, to allow an InputArchive to be used. This properly separates I/O and CPU - the deserialisation involving an InputArchive is pure CPU. E.g. we could use a single I/O thread to read serial elements into memory, and queue pure CPU deserialisation jobs that involve an InputArchive.
Note that the system already needs a pure CPU thread pool, so it makes sense for the PersistStore to use that rather than create its own. This also means that multiple PersistStores share the same CPU thread pool.
It is proposed that the cxThread thread pool is used and a rather direct API on this is available, and layered on top of that is a per-CSpace thread pool that knows about visiting IObjects in that CSpace. It is not clear which one the PSpace would use. Preferably the job would own the object being deserialised, and it is only registered in the CSpace at the end, so there is no need to visit it.
It might be possible to somehow queue up deserialised objects, and only use a single CSpace lock to 'apply' them in a batch. This reduces some overheads, and might be appropriate when there are many tiny objects loaded at a rate of 1MHz or more
Job queue
It would seem that the job queue only needs to record a queue of oids. However it would be more practical to store a queue of pointers to DGIndepNodeForAsyncPref. This in turn provides the oid, but also allows ROT::OnAsyncLoadComplete() to be called, passing the address of the DGIndepNodeForAsyncPref, avoiding the need to look it up in the ROT's map.
AsyncObjectLoader
The AsyncObjectLoader thread is responsible for asynchronously loading objects from the LSS.
Correct usage of the AsyncObjectLoader involves
- A single call to Start()
- Any number of calls to Push()
- A single call to Stop(). This must come after all calls to Push().
For each job that is pushed, job->Execute() is called exactly once. This allows an implementation of Execute to decrement a ref count, to allow the object to be reclaimed after there is no risk that Execute be called.
AsyncObjectLoader.h
// AsyncObjectLoader.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2011
@import "PSpace.h"
@import "DGIndepNodeForAsyncPref.h"
#include "Ceda/cxThread/AsyncJobQueueOnThread.h"
namespace ceda
{
struct AsyncLoadJob
{
AsyncLoadJob(DGIndepNodeForAsyncPref* node = nullptr) : node(node) {}
void operator()() { node->AsyncLoad(); }
DGIndepNodeForAsyncPref* node;
};
class AsyncLoadThread : public AsyncJobQueueOnThread<AsyncLoadJob> {};
struct AsyncUpdateRotJob
{
AsyncUpdateRotJob(DGIndepNodeForAsyncPref* node = nullptr) : node(node) {}
void operator()() { node->AsyncUpdateRot(); }
DGIndepNodeForAsyncPref* node;
};
class AsyncUpdateRotThread : public AsyncJobQueueOnThread<AsyncUpdateRotJob> {};
} // namespace ceda
AsyncObjectLoader.cpp
// AsyncObjectLoader.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2011
@import "AsyncObjectLoader.h"
namespace ceda
{
} // namespace ceda