package freenet.client.async; /** We need to be able to suspend execution of jobs changing persistent state in order to write * it to disk consistently. Also, some jobs may want to request immediate serialization. However, * it is safe to call functions that do not modify the persistent state from any thread. E.g. * choosing a key to fetch via SendableRequest.chooseKey(). * * The key point here is that we don't allow any jobs to run while we are doing a checkpoint. There * are some further complications related to atomicity, e.g. queueInternal(). */ public interface PersistentJobRunner { /** Start a job immediately unless we are about to write a checkpoint. If we are, queue the job * until after the checkpoint has completed. We do not store the jobs, so this should be used * for tasks which either are "external" and so will be retried after an unclean shutdown (for * example, fetching a block from the datastore or the network), or where we will check for the * situation on restart in onResume() (for example, the callback that starts a FEC decode). * This should also be used for e.g. freeing on-disk files after a checkpoint. * @param persistentJob The job to run now or after the checkpoint. * @param threadPriority The priority of the job. * @throws PersistenceDisabledException If persistence is disabled. */ void queue(PersistentJob persistentJob, int threadPriority) throws PersistenceDisabledException; /** Queue the job at low thread priority or drop it if persistence is disabled. */ void queueNormalOrDrop(PersistentJob persistentJob); /** Start an "internal" job. We will not checkpoint until all the internal jobs have finished; * we do not queue them at all. Hence a series of internal jobs is atomic. This should be used * for stuff like creating the next stage of a request, where storing the half-way state would * lead to it potentially not restarting properly after shutdown. It MUST NOT be used for * events from outside the client layer, including finding blocks in the datastore, on the * network etc. * * FIXME this doesn't queue at all. Come up with a better name! :) * @param job * @throws PersistenceDisabledException */ void queueInternal(PersistentJob job, int threadPriority) throws PersistenceDisabledException; /** Start an "internal" job. We will not checkpoint until all the internal jobs have finished; * we do not queue them at all. Hence a series of internal jobs is atomic. This should be used * for stuff like creating the next stage of a request, where storing the half-way state would * lead to it potentially not restarting properly after shutdown. It MUST NOT be used for * events from outside the client layer, including finding blocks in the datastore, on the * network etc. * * Often when we call this we could have continued the job on the same thread. That's not * always the best thing to do however, we often move stuff to another job to minimise locking * or increase throughput. * * FIXME this doesn't queue at all. Come up with a better name! :) * @param job */ void queueInternal(PersistentJob job); /** Commit ASAP. Can also be set via returning true from a PersistentJob, but it's useful to be * able to do it "inline". */ void setCheckpointASAP(); /** Has the queue started yet? */ boolean hasLoaded(); interface CheckpointLock { /** The job is finished, unlock, allow a checkpoint if necessary. * @param forceWrite True to force a checkpoint ASAP. * @param threadPriority The priority of the calling thread. */ void unlock(boolean forceWrite, int threadPriority); } /** Obtain a lock which will prevent checkpointing until it is unlocked. This counts as a * thread, and can be used for e.g. MemoryLimitedJob's that can change persistent data. Like * any lock, you MUST use a try/finally block. lock() may block if a checkpoint is in progress * or is scheduled. * @throws PersistenceDisabledException If we are unable to lock because the system is shutting * down. */ public CheckpointLock lock() throws PersistenceDisabledException; /** For persistent requests, return true if the bloom filter salt has changed when loading the * requests. In which case all the Bloom filters will be invalid and will need to be * recomputed. */ boolean newSalt(); /** If true, the node is shutting down */ boolean shuttingDown(); }