Stxxl  1.4.0
include/stxxl/bits/mng/block_scheduler.h
Go to the documentation of this file.
00001 /***************************************************************************
00002  *  include/stxxl/bits/mng/block_scheduler.h
00003  *
00004  *  Part of the STXXL. See http://stxxl.sourceforge.net
00005  *
00006  *  Copyright (C) 2010-2011 Raoul Steffen <R-Steffen@gmx.de>
00007  *
00008  *  Distributed under the Boost Software License, Version 1.0.
00009  *  (See accompanying file LICENSE_1_0.txt or copy at
00010  *  http://www.boost.org/LICENSE_1_0.txt)
00011  **************************************************************************/
00012 
00013 #ifndef STXXL_BLOCK_SCHEDULER_HEADER
00014 #define STXXL_BLOCK_SCHEDULER_HEADER
00015 
00016 #include <stack>
00017 #include <queue>
00018 #include <limits>
00019 
00020 #include <stxxl/bits/mng/mng.h>
00021 #include <stxxl/bits/mng/typed_block.h>
00022 #include <stxxl/bits/common/addressable_queues.h>
00023 
00024 __STXXL_BEGIN_NAMESPACE
00025 
00026 //! \brief Virtualization of a block of data.
00027 //! Holds information for allocating and swapping. To use in cooperation with block_scheduler.
00028 //!
00029 //! A swappable_block can be uninitialized, i.e. it holds no data.
00030 //! When access is required, is has to be acquired first, and released afterwards, so it can be swapped in and out as required.
00031 //! If the stored data is no longer needed, it can get uninitialized, freeing both internal and external memory.
00032 //! \tparam ValueType type of contained objects (POD with no references to internal memory).
00033 //! \tparam BlockSize Number of objects in one block.
00034 //!         BlockSize*sizeof(ValueType) must be divisible by 4096.
00035 template <typename ValueType, unsigned BlockSize>
00036 class swappable_block
00037 {
00038 protected:
00039     static const unsigned_type raw_block_size = BlockSize * sizeof(ValueType);
00040 public:
00041     typedef typed_block<raw_block_size, ValueType> internal_block_type;
00042     typedef typename internal_block_type::bid_type external_block_type;
00043 
00044 protected:
00045     external_block_type external_data;      //!external_data.valid if no associated space on disk
00046     internal_block_type * internal_data;    //NULL if there is no internal memory reserved
00047     bool dirty;
00048     int_type reference_count;
00049 
00050     static unsigned_type disk_allocation_offset;
00051 
00052     void get_external_block()
00053     { block_manager::get_instance()->new_block(striping(), external_data, ++disk_allocation_offset); }
00054 
00055     void free_external_block()
00056     {
00057         block_manager::get_instance()->delete_block(external_data);
00058         external_data = external_block_type(); // make invalid
00059     }
00060 
00061 public:
00062     //! \brief Create in uninitialized state.
00063     swappable_block()
00064         : external_data() /*!valid*/, internal_data(0), dirty(false), reference_count(0) {}
00065 
00066     ~swappable_block() {}
00067 
00068     //! \brief If it has an internal_block. The internal_block implicitly holds valid data.
00069     bool is_internal() const
00070     { return internal_data; }
00071 
00072     //! \brief If the external_block does not hold valid data.
00073     bool is_dirty() const
00074     { return dirty; }
00075 
00076     //! \brief If it has an external_block.
00077     bool has_external_block() const
00078     { return external_data.valid(); }
00079 
00080     //! \brief If it has an external_block that holds valid data.
00081     bool is_external() const
00082     { return has_external_block() && ! is_dirty(); }
00083 
00084     //! \brief If it is acquired.
00085     bool is_acquired() const
00086     { return reference_count > 0; }
00087 
00088     //! \brief If it holds an internal_block but does not need it.
00089     bool is_evictable() const
00090     { return ! is_acquired() && is_internal(); }
00091 
00092     //! \brief If it has some valid data (in- or external).
00093     bool is_initialized() const
00094     { return is_internal() || is_external(); }
00095 
00096     //! \brief Invalidate external data if true.
00097     //! \return is_dirty()
00098     bool make_dirty_if(const bool make_dirty)
00099     {
00100         assert(is_acquired());
00101         return dirty = make_dirty || dirty;
00102     }
00103 
00104     //! \brief Acquire the block, i.e. add a reference. Has to be internal.
00105     //! \return A reference to the data-block.
00106     internal_block_type & acquire()
00107     {
00108         assert(is_internal());
00109         ++ reference_count;
00110         return * internal_data;
00111     }
00112 
00113     //! \brief Release the block, i.e. subduct a reference. Has to be acquired.
00114     void release()
00115     {
00116         assert(is_acquired());
00117         -- reference_count;
00118     }
00119 
00120     //! \brief Get a reference to the data-block. Has to be acquired.
00121     const internal_block_type & get_internal_block() const
00122     {
00123         assert(is_acquired());
00124         return * internal_data;
00125     }
00126 
00127     //! \brief Get a reference to the data-block. Has to be acquired.
00128     internal_block_type & get_internal_block()
00129     {
00130         assert(is_acquired());
00131         return * internal_data;
00132     }
00133 
00134     //! \brief Fill block with default data, is supposed to be overwritten by subclass. Has to be internal.
00135     void fill_default() {}
00136 
00137     //! \brief Read asyncronusly from external_block to internal_block. Has to be internal and have an external_block.
00138     //! \return A request pointer to the I/O.
00139     request_ptr read_async(completion_handler on_cmpl = default_completion_handler())
00140     {
00141         assert(is_internal());
00142         assert(has_external_block());
00143         #ifdef RW_VERBOSE
00144         STXXL_MSG("reading block");
00145         #endif
00146         dirty = false;
00147         return internal_data->read(external_data, on_cmpl);
00148     }
00149 
00150     //! \brief Read synchronously from external_block to internal_block. Has to be internal and have an external_block.
00151     void read_sync()
00152     { read_async()->wait(); }
00153 
00154     //! \brief Write asyncronusly from internal_block to external_block if necessary.
00155     //! \return A request pointer to the I/O, an invalid request pointer if not necessary.
00156     request_ptr clean_async(completion_handler on_cmpl = default_completion_handler())
00157     {
00158         if (! is_dirty())
00159             return request_ptr();
00160         if (! has_external_block())
00161             get_external_block();
00162         #ifdef RW_VERBOSE
00163         STXXL_MSG("writing block");
00164         #endif
00165         dirty = false;
00166         return internal_data->write(external_data, on_cmpl);
00167     }
00168 
00169     //! \brief Write synchronously from internal_block to external_block if necessary.
00170     void clean_sync()
00171     {
00172         request_ptr rp = clean_async();
00173         if (rp.valid())
00174             rp->wait();
00175     }
00176 
00177     //! \brief Attach an internal_block, making the block internal. Has to be not internal.
00178     void attach_internal_block(internal_block_type * iblock)
00179     {
00180         assert(! is_internal());
00181         internal_data = iblock;
00182     }
00183 
00184     //! \brief Detach the internal_block. Writes to external_block if necessary. Has to be evictable.
00185     //! \return A pointer to the internal_block.
00186     internal_block_type * detach_internal_block()
00187     {
00188         assert(is_evictable());
00189         clean_sync();
00190         internal_block_type * iblock = internal_data;
00191         internal_data = 0;
00192         return iblock;
00193     }
00194 
00195     //! \brief Bring the block in uninitialized state, freeing external and internal memory.
00196     //! Returns a pointer to the internal_block, NULL if it had none.
00197     //! \return A pointer to the freed internal_block, NULL if it had none.
00198     internal_block_type * deinitialize()
00199     {
00200         assert(! is_acquired());
00201         dirty = false;
00202         // free external_block (so that it becomes invalid and the disk-space can be used again)
00203         if (has_external_block())
00204             free_external_block();
00205         // free internal_block
00206         internal_block_type * iblock = internal_data;
00207         internal_data = 0;
00208         return iblock;
00209     }
00210 
00211     //! \brief Set the external_block that holds the swappable_block's data. The block gets initialized with it.
00212     //! \param eblock The external_block holding initial data.
00213     void initialize(external_block_type eblock)
00214     {
00215         assert(! is_initialized());
00216         external_data = eblock;
00217     }
00218 
00219     //! \brief Extract the swappable_blocks data in an external_block. The block gets uninitialized.
00220     //! \return The external_block that holds the swappable_block's data.
00221     external_block_type extract_external_block()
00222     {
00223         assert(! is_internal());
00224         external_block_type eblock = external_data;
00225         external_data = external_block_type();
00226         return eblock;
00227     }
00228 };
00229 
00230 template <typename ValueType, unsigned BlockSize>
00231 unsigned_type swappable_block<ValueType, BlockSize>::disk_allocation_offset = 0;
00232 
00233 template <class SwappableBlockType> class block_scheduler_algorithm;
00234 template <class SwappableBlockType> class block_scheduler_algorithm_online_lru;
00235 
00236 //! \brief Schedules swapping of blocks and provides blocks for temporary storage.
00237 //!
00238 //! In simple mode, it tries to save I/Os through caching only.
00239 //! In simulation mode, it records access patterns into a prediction sequence.
00240 //! The prediction sequence can then be used for prefetching in the (offline) execute mode.
00241 //! This will only work for algorithms with deterministic, data oblivious access patterns.
00242 //! In simulation mode, no I/O is performed; the data provided is accessible but undefined.
00243 //! In execute mode, it does caching, prefetching, and possibly other optimizations.
00244 //! \tparam SwappableBlockType Type of swappable_blocks to manage. Can be some specialized subclass.
00245 template <class SwappableBlockType>
00246 class block_scheduler : private noncopyable
00247 {
00248 protected:
00249     // tuning-parameter: To acquire blocks, internal memory has to be allocated.
00250     // This constant limits the number of internal_blocks allocated at once.
00251     static const int_type max_internal_blocks_alloc_at_once;
00252 
00253     typedef int_type time_type;
00254 
00255 public:
00256     typedef typename SwappableBlockType::internal_block_type internal_block_type;
00257     typedef typename SwappableBlockType::external_block_type external_block_type;
00258     typedef typename std::vector<SwappableBlockType>::size_type swappable_block_identifier_type;
00259 
00260     /*/! Mode the block scheduler currently works in
00261     enum mode
00262     {
00263         online,         //serve requests immediately, without any prediction, LRU caching
00264         simulation,     //record prediction sequence only, do not serve requests, (returned blocks must not be accessed)
00265         offline_lfd,    //serve requests based on prediction sequence, using longest-forward-distance caching
00266         offline_lfd_prefetch     //serve requests based on prediction sequence, using longest-forward-distance caching, and prefetching
00267     };*/
00268 
00269 public:
00270     // -------- prediction_sequence -------
00271     enum block_scheduler_operation
00272     {
00273         op_acquire,
00274         op_acquire_uninitialized,
00275         op_release,
00276         op_release_dirty,
00277         op_deinitialize,
00278         op_initialize,
00279         op_extract_external_block
00280     };
00281 
00282     struct prediction_sequence_element
00283     {
00284         block_scheduler_operation op;
00285         swappable_block_identifier_type id;
00286         time_type time;
00287 
00288         prediction_sequence_element(block_scheduler_operation op,
00289                 swappable_block_identifier_type id, int_type time)
00290             : op(op), id(id), time(time) {}
00291 
00292     };
00293 
00294     typedef std::deque<prediction_sequence_element> prediction_sequence_type;
00295     // ---- end prediction_sequence -------
00296 
00297 protected:
00298     template <class SBT> friend class block_scheduler_algorithm;
00299 
00300     const int_type max_internal_blocks;
00301     int_type remaining_internal_blocks;
00302     //! \brief Stores pointers to arrays of internal_blocks. Used to deallocate them only.
00303     std::stack<internal_block_type *> internal_blocks_blocks;
00304     //! \brief holds free internal_blocks with attributes reset
00305     std::stack<internal_block_type * > free_internal_blocks;
00306     //! \brief temporary blocks that will not be needed after algorithm termination
00307     mutable std::vector<SwappableBlockType> swappable_blocks;
00308     //! \brief holds indices of free swappable_blocks with attributes reset
00309     std::priority_queue<swappable_block_identifier_type, std::vector<swappable_block_identifier_type>,
00310             std::greater<swappable_block_identifier_type> > free_swappable_blocks;
00311     block_manager * bm;
00312     block_scheduler_algorithm<SwappableBlockType> * algo;
00313 
00314     //! \brief Get an internal_block from the freelist or a newly allocated one if available.
00315     //! \return Pointer to the internal_block. NULL if none available.
00316     internal_block_type * get_free_internal_block()
00317     {
00318         if (! free_internal_blocks.empty())
00319         {
00320             // => there are internal_blocks in the free-list
00321             internal_block_type * iblock = free_internal_blocks.top();
00322             free_internal_blocks.pop();
00323             return iblock;
00324         }
00325         else if (remaining_internal_blocks > 0)
00326         {
00327             // => more internal_blocks can be allocated
00328             int_type num_blocks = std::min(max_internal_blocks_alloc_at_once, remaining_internal_blocks);
00329             remaining_internal_blocks -= num_blocks;
00330             internal_block_type * iblocks = new internal_block_type[num_blocks];
00331             internal_blocks_blocks.push(iblocks);
00332             for (int_type i = num_blocks - 1; i > 0; --i)
00333                 free_internal_blocks.push(iblocks + i);
00334             return iblocks;
00335         }
00336         else
00337         {
00338             // => no internal_block available
00339             return 0;
00340         }
00341     }
00342 
00343     //! \brief Return an internal_block to the freelist.
00344     void return_free_internal_block(internal_block_type * iblock)
00345     { free_internal_blocks.push(iblock); }
00346 
00347 public:
00348     //! \brief create a block_scheduler with empty prediction sequence in simple mode.
00349     //! \param max_internal_memory Amount of internal memory (in bytes) the scheduler is allowed to use for acquiring, prefetching and caching.
00350     explicit block_scheduler(const int_type max_internal_memory)
00351         : max_internal_blocks(div_ceil(max_internal_memory, sizeof(internal_block_type))),
00352           remaining_internal_blocks(max_internal_blocks),
00353           bm(block_manager::get_instance()),
00354           algo(0)
00355     { algo = new block_scheduler_algorithm_online_lru<SwappableBlockType>(*this); }
00356 
00357     ~block_scheduler()
00358     {
00359         delete algo;
00360         int_type num_freed_internal_blocks = 0;
00361         if (free_swappable_blocks.size() != swappable_blocks.size())
00362         {
00363             // => not all swappable_blocks are free, at least deinitialize them
00364             STXXL_ERRMSG("not all swappable_blocks are free, those not acquired will be deinitialized");
00365             for (typename std::vector<SwappableBlockType>::iterator it = swappable_blocks.begin(); // evictable_blocks would suffice
00366                     it != swappable_blocks.end(); ++it)
00367                 if (! it->is_acquired())
00368                     num_freed_internal_blocks += bool(it->deinitialize()); // count internal_blocks that get freed
00369         }
00370         if (int_type nlost = (max_internal_blocks - remaining_internal_blocks)
00371                 - (free_internal_blocks.size() + num_freed_internal_blocks))
00372             STXXL_ERRMSG(nlost << " internal_blocks are lost. They will get deallocated.");
00373         while (! internal_blocks_blocks.empty())
00374         {
00375             delete [] internal_blocks_blocks.top();
00376             internal_blocks_blocks.pop();
00377         }
00378     }
00379 
00380     //! \brief Acquire the given block.
00381     //! Has to be in pairs with release. Pairs may be nested and interleaved.
00382     //! \return Reference to the block's data.
00383     //! \param sblock Swappable block to acquire.
00384     internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false)
00385     { return algo->acquire(sbid, uninitialized); }
00386 
00387     //! \brief Release the given block.
00388     //! Has to be in pairs with acquire. Pairs may be nested and interleaved.
00389     //! \param sblock Swappable block to release.
00390     //! \param dirty If the data has been changed, invalidating possible data in external storage.
00391     void release(const swappable_block_identifier_type sbid, const bool dirty)
00392     { algo->release(sbid, dirty); }
00393 
00394     //! \brief Drop all data in the given block, freeing in- and external memory.
00395     void deinitialize(const swappable_block_identifier_type sbid)
00396     { algo->deinitialize(sbid); }
00397 
00398     //! \brief Initialize the swappable_block with the given external_block.
00399     //!
00400     //! It will use the the external_block for swapping and take care about it's deallocation. Has to be uninitialized.
00401     //! \param sbid identifier to the swappable_block
00402     //! \param eblock external_block a.k.a. bid
00403     void initialize(const swappable_block_identifier_type sbid, external_block_type eblock)
00404     { algo->initialize(sbid, eblock); }
00405 
00406     //! \brief deinitialize the swappable_block and return it's contents in an external_block.
00407     //!
00408     //! \param sbid identifier to the swappable_block
00409     //! \return external_block a.k.a. bid
00410     external_block_type extract_external_block(const swappable_block_identifier_type sbid)
00411     { return algo->extract_external_block(sbid); }
00412 
00413     //! \brief check if the swappable_block is initialized
00414     //! \param sbid identifier to the swappable_block
00415     //! \return if the swappable_block is initialized
00416     bool is_initialized(const swappable_block_identifier_type sbid) const
00417     { return algo->is_initialized(sbid); }
00418 
00419     //! \brief Record a timestep in the prediction sequence to seperate consecutive acquire rsp. release-operations.
00420     //! Has an effect only in simulation mode.
00421     void explicit_timestep()
00422     { algo->explicit_timestep(); }
00423 
00424     //! \brief Get a const reference to given block's data. Block has to be already acquired.
00425     //! \param sblock Swappable block to access.
00426     internal_block_type & get_internal_block(const swappable_block_identifier_type sbid) const
00427     { return swappable_blocks[sbid].get_internal_block(); }
00428 
00429     //! \brief Allocate an uninitialized swappable_block.
00430     //! \return An identifier of the block.
00431     swappable_block_identifier_type allocate_swappable_block()
00432     {
00433         swappable_block_identifier_type sbid;
00434         if (free_swappable_blocks.empty())
00435         {
00436             // create new swappable_block
00437             sbid = swappable_blocks.size();
00438             swappable_blocks.resize(sbid + 1);
00439             algo->swappable_blocks_resize(sbid + 1);
00440         }
00441         else
00442         {
00443             // take swappable_block from freelist
00444             sbid = free_swappable_blocks.top();
00445             free_swappable_blocks.pop();
00446         }
00447         return sbid;
00448     }
00449 
00450     //! \brief Free given no longer used temporary swappable_block.
00451     //! \param sblock Temporary swappable_block to free.
00452     void free_swappable_block(const swappable_block_identifier_type sbid)
00453     {
00454         deinitialize(sbid);
00455         free_swappable_blocks.push(sbid);
00456     }
00457 
00458     //! \brief Returns if simulation mode is on, i.e. if a prediction sequence is being recorded.
00459     //! \return If simulation mode is on.
00460     bool is_simulating() const
00461     { return algo->is_simulating(); }
00462 
00463     //! \brief Switch the used algorithm, e.g. to simulation etc..
00464     //! \param new_algo Pointer to the new algorithm object. Has to be instantiated to the block scheduler (or the old algorithm object).
00465     //! \return Pointer to the old algorithm object.
00466     block_scheduler_algorithm<SwappableBlockType> * switch_algorithm_to(block_scheduler_algorithm<SwappableBlockType> * new_algo)
00467     {
00468         assert(&new_algo->bs == this);
00469         block_scheduler_algorithm<SwappableBlockType> * old_algo = algo;
00470         algo = new_algo;
00471         return old_algo;
00472     }
00473 
00474     //! \brief get the prediction_sequence
00475     //! \return reference to the prediction_sequence
00476     const prediction_sequence_type & get_prediction_sequence() const
00477     { return algo->get_prediction_sequence(); }
00478 
00479     void flush()
00480     {
00481         std::vector<request_ptr> requests;
00482         while (! algo->evictable_blocks_empty())
00483         {
00484             swappable_block_identifier_type sbid = algo->evictable_blocks_pop();
00485             request_ptr rq = swappable_blocks[sbid].clean_async();
00486             if (rq.valid())
00487                 requests.push_back(rq);
00488             return_free_internal_block(swappable_blocks[sbid].detach_internal_block());
00489         }
00490         for (typename std::vector<request_ptr>::reverse_iterator it = requests.rbegin();
00491                 it != requests.rend(); ++it)
00492             (*it)->wait();
00493     }
00494 };
00495 
00496 template <class SwappableBlockType>
00497 const int_type block_scheduler<SwappableBlockType>::max_internal_blocks_alloc_at_once = 128;
00498 
00499 //! \brief Interface of a block scheduling algorithm.
00500 template <class SwappableBlockType>
00501 class block_scheduler_algorithm : private noncopyable
00502 {
00503 protected:
00504     typedef block_scheduler<SwappableBlockType> block_scheduler_type;
00505     typedef typename block_scheduler_type::internal_block_type internal_block_type;
00506     typedef typename block_scheduler_type::external_block_type external_block_type;
00507     typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type;
00508     typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type;
00509     typedef typename block_scheduler_type::time_type time_type;
00510 
00511 public:
00512     block_scheduler_type & bs;
00513 protected:
00514     std::vector<SwappableBlockType> & swappable_blocks;
00515     prediction_sequence_type prediction_sequence;
00516 
00517     block_scheduler_algorithm * get_algorithm_from_block_scheduler()
00518     { return bs.algo; }
00519 
00520     //! \brief Get an internal_block from the block_scheduler.
00521     //! \return Pointer to the internal_block. NULL if none available.
00522     internal_block_type * get_free_internal_block_from_block_scheduler()
00523     { return bs.get_free_internal_block(); }
00524 
00525     //! \brief Return an internal_block to the block_scheduler.
00526     void return_free_internal_block_to_block_scheduler(internal_block_type * iblock)
00527     { bs.return_free_internal_block(iblock); }
00528 
00529 public:
00530     block_scheduler_algorithm(block_scheduler_type & bs)
00531         : bs(bs),
00532           swappable_blocks(bs.swappable_blocks)
00533     {}
00534 
00535     block_scheduler_algorithm(block_scheduler_algorithm * old)
00536         : bs(old->bs),
00537           swappable_blocks(bs.swappable_blocks)
00538     {}
00539 
00540     virtual ~block_scheduler_algorithm() {};
00541 
00542     virtual bool evictable_blocks_empty() = 0;
00543     virtual swappable_block_identifier_type evictable_blocks_pop() = 0;
00544     virtual void swappable_blocks_resize(swappable_block_identifier_type /*size*/) {}
00545 
00546     virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) = 0;
00547     virtual void release(swappable_block_identifier_type sbid, const bool dirty) = 0;
00548     virtual void deinitialize(swappable_block_identifier_type sbid) = 0;
00549     virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) = 0;
00550     virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) = 0;
00551 
00552     virtual bool is_initialized(const swappable_block_identifier_type sbid) const
00553         { return swappable_blocks[sbid].is_initialized(); }
00554 
00555     virtual void explicit_timestep() {}
00556     virtual bool is_simulating() const
00557         { return false; }
00558     virtual const prediction_sequence_type & get_prediction_sequence() const
00559         { return prediction_sequence; }
00560 };
00561 
00562 //! \brief Block scheduling algorithm caching via the least recently used policy (online).
00563 template <class SwappableBlockType>
00564 class block_scheduler_algorithm_online_lru : public block_scheduler_algorithm<SwappableBlockType>
00565 {
00566 protected:
00567     typedef block_scheduler<SwappableBlockType> block_scheduler_type;
00568     typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type;
00569     typedef typename block_scheduler_type::internal_block_type internal_block_type;
00570     typedef typename block_scheduler_type::external_block_type external_block_type;
00571     typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type;
00572 
00573     using block_scheduler_algorithm_type::bs;
00574     using block_scheduler_algorithm_type::swappable_blocks;
00575     using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler;
00576     using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler;
00577     using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler;
00578 
00579     //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired.
00580     addressable_fifo_queue<swappable_block_identifier_type> evictable_blocks;
00581 
00582     internal_block_type * get_free_internal_block()
00583     {
00584         // try to get a free internal_block
00585         if (internal_block_type * iblock = get_free_internal_block_from_block_scheduler())
00586             return iblock;
00587         // evict block
00588         assert(! evictable_blocks.empty()); // fails it there is not enough memory available
00589         return swappable_blocks[evictable_blocks.pop()].detach_internal_block();
00590     }
00591 
00592     void return_free_internal_block(internal_block_type * iblock)
00593     { return_free_internal_block_to_block_scheduler(iblock); }
00594 
00595     void init()
00596     {
00597         if (get_algorithm_from_block_scheduler())
00598             while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty())
00599                 evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop());
00600     }
00601 
00602 public:
00603     block_scheduler_algorithm_online_lru(block_scheduler_type & bs)
00604         : block_scheduler_algorithm_type(bs)
00605     { init(); }
00606 
00607     block_scheduler_algorithm_online_lru(block_scheduler_algorithm_type * old)
00608         : block_scheduler_algorithm_type(old)
00609     { init(); }
00610 
00611     virtual ~block_scheduler_algorithm_online_lru()
00612     {
00613         if (! evictable_blocks.empty())
00614             STXXL_ERRMSG("Destructing block_scheduler_algorithm_online that still holds evictable blocks. They get deinitialized.");
00615         while (! evictable_blocks.empty())
00616         {
00617             SwappableBlockType & sblock = swappable_blocks[evictable_blocks.pop()];
00618             if (internal_block_type * iblock = sblock.deinitialize())
00619                 return_free_internal_block(iblock);
00620         }
00621     }
00622 
00623     virtual bool evictable_blocks_empty()
00624     { return evictable_blocks.empty(); }
00625 
00626     virtual swappable_block_identifier_type evictable_blocks_pop()
00627     { return evictable_blocks.pop(); }
00628 
00629     virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false)
00630     {
00631         SwappableBlockType & sblock = swappable_blocks[sbid];
00632         /* acquired => internal -> increase reference count
00633            internal but not acquired -> remove from evictable_blocks, increase reference count
00634            not internal => uninitialized or external -> get internal_block, increase reference count
00635            uninitialized -> fill with default value
00636            external -> read */
00637         if (sblock.is_internal())
00638         {
00639             if (! sblock.is_acquired())
00640                 // not acquired yet -> remove from evictable_blocks
00641                 evictable_blocks.erase(sbid);
00642             sblock.acquire();
00643         }
00644         else if (sblock.is_initialized())
00645         {
00646             // => external but not internal
00647             //get internal_block
00648             sblock.attach_internal_block(get_free_internal_block());
00649             if (! uninitialized)
00650                 //load block synchronously
00651                 sblock.read_sync();
00652             sblock.acquire();
00653         }
00654         else
00655         {
00656             // => ! sblock.is_initialized()
00657             //get internal_block
00658             sblock.attach_internal_block(get_free_internal_block());
00659             sblock.acquire();
00660             //initialize new block
00661             if (! uninitialized)
00662                 sblock.fill_default();
00663         }
00664         return sblock.get_internal_block();
00665     }
00666 
00667     virtual void release(swappable_block_identifier_type sbid, const bool dirty)
00668     {
00669         SwappableBlockType & sblock = swappable_blocks[sbid];
00670         sblock.make_dirty_if(dirty);
00671         sblock.release();
00672         if (! sblock.is_acquired())
00673         {
00674             if (sblock.is_dirty() || sblock.is_external())
00675                 // => evictable, put in pq
00676                 evictable_blocks.insert(sbid);
00677             else
00678                 // => uninitialized, release internal block and put it in freelist
00679                 return_free_internal_block(sblock.detach_internal_block());
00680         }
00681     }
00682 
00683     virtual void deinitialize(swappable_block_identifier_type sbid)
00684     {
00685         SwappableBlockType & sblock = swappable_blocks[sbid];
00686         if (sblock.is_evictable())
00687             evictable_blocks.erase(sbid);
00688         if (internal_block_type * iblock = sblock.deinitialize())
00689             return_free_internal_block(iblock);
00690     }
00691 
00692     virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock)
00693     {
00694         SwappableBlockType & sblock = swappable_blocks[sbid];
00695         sblock.initialize(eblock);
00696     }
00697 
00698     virtual external_block_type extract_external_block(swappable_block_identifier_type sbid)
00699     {
00700         SwappableBlockType & sblock = swappable_blocks[sbid];
00701         if (sblock.is_evictable())
00702             evictable_blocks.erase(sbid);
00703         if (sblock.is_internal())
00704             return_free_internal_block(sblock.detach_internal_block());
00705         return sblock.extract_external_block();
00706     }
00707 };
00708 
00709 //! \brief Pseudo block scheduling algorithm only recording the request sequence.
00710 template <class SwappableBlockType>
00711 class block_scheduler_algorithm_simulation : public block_scheduler_algorithm<SwappableBlockType>
00712 {
00713 protected:
00714     typedef block_scheduler<SwappableBlockType> block_scheduler_type;
00715     typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type;
00716     typedef typename block_scheduler_type::internal_block_type internal_block_type;
00717     typedef typename block_scheduler_type::external_block_type external_block_type;
00718     typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type;
00719     typedef typename block_scheduler_type::prediction_sequence_element prediction_sequence_element_type;
00720     typedef typename block_scheduler_algorithm_type::time_type time_type;
00721 
00722     using block_scheduler_algorithm_type::bs;
00723     using block_scheduler_algorithm_type::prediction_sequence;
00724     using block_scheduler_algorithm_type::swappable_blocks;
00725     using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler;
00726     using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler;
00727     using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler;
00728 
00729     //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired.
00730     std::stack<swappable_block_identifier_type> evictable_blocks;
00731     time_type time_count;
00732     bool last_op_release;
00733     std::vector<int_type> reference_counts;
00734     internal_block_type dummy_block;
00735 
00736     void return_free_internal_block(internal_block_type * iblock)
00737     { return_free_internal_block_to_block_scheduler(iblock); }
00738 
00739     void init()
00740     {
00741         if (get_algorithm_from_block_scheduler())
00742             while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty())
00743                 evictable_blocks.push(get_algorithm_from_block_scheduler()->evictable_blocks_pop());
00744         for (swappable_block_identifier_type i = 0; i < reference_counts.size(); ++i)
00745             reference_counts[i] = swappable_blocks[i].is_initialized();
00746     }
00747 
00748 public:
00749     block_scheduler_algorithm_simulation(block_scheduler_type & bs)
00750         : block_scheduler_algorithm_type(bs),
00751           time_count(0),
00752           last_op_release(false),
00753           reference_counts(swappable_blocks.size())
00754     { init(); }
00755 
00756     block_scheduler_algorithm_simulation(block_scheduler_algorithm_type * old)
00757         : block_scheduler_algorithm_type(old),
00758           time_count(0),
00759           last_op_release(false),
00760           reference_counts(swappable_blocks.size())
00761     { init(); }
00762 
00763     virtual ~block_scheduler_algorithm_simulation()
00764     {
00765         if (! evictable_blocks.empty())
00766             STXXL_ERRMSG("Destructing block_scheduler_algorithm_record_prediction_sequence that still holds evictable blocks. They get deinitialized.");
00767         while (! evictable_blocks.empty())
00768         {
00769             SwappableBlockType & sblock = swappable_blocks[evictable_blocks.top()];
00770             if (internal_block_type * iblock = sblock.deinitialize())
00771                 return_free_internal_block(iblock);
00772             evictable_blocks.pop();
00773         }
00774     }
00775 
00776     virtual bool evictable_blocks_empty()
00777     { return evictable_blocks.empty(); }
00778 
00779     virtual swappable_block_identifier_type evictable_blocks_pop()
00780     {
00781         swappable_block_identifier_type sbid = evictable_blocks.top();
00782         evictable_blocks.pop();
00783         return sbid;
00784     }
00785 
00786     virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false)
00787     {
00788         ++reference_counts[sbid];
00789         last_op_release = false;
00790         if (uninitialized)
00791             prediction_sequence.push_back(prediction_sequence_element_type
00792                     (block_scheduler_type::op_acquire_uninitialized, sbid, time_count));
00793         else
00794             prediction_sequence.push_back(prediction_sequence_element_type
00795                     (block_scheduler_type::op_acquire, sbid, time_count));
00796         return dummy_block;
00797     }
00798 
00799     virtual void release(swappable_block_identifier_type sbid, const bool dirty)
00800     {
00801         --reference_counts[sbid] += dirty;
00802         time_count += ! last_op_release;
00803         last_op_release = true;
00804         if (dirty)
00805             prediction_sequence.push_back(prediction_sequence_element_type
00806                     (block_scheduler_type::op_release_dirty, sbid, time_count));
00807         else
00808             prediction_sequence.push_back(prediction_sequence_element_type
00809                     (block_scheduler_type::op_release, sbid, time_count));
00810     }
00811 
00812     virtual void deinitialize(swappable_block_identifier_type sbid)
00813     {
00814         reference_counts[sbid] = false;
00815         prediction_sequence.push_back(prediction_sequence_element_type
00816                 (block_scheduler_type::op_deinitialize, sbid, time_count));
00817     }
00818 
00819     virtual void initialize(swappable_block_identifier_type sbid, external_block_type)
00820     {
00821         reference_counts[sbid] = true;
00822         prediction_sequence.push_back(prediction_sequence_element_type
00823                 (block_scheduler_type::op_initialize, sbid, time_count));
00824     }
00825 
00826     virtual external_block_type extract_external_block(swappable_block_identifier_type sbid)
00827     {
00828         reference_counts[sbid] = false;
00829         prediction_sequence.push_back(prediction_sequence_element_type
00830                 (block_scheduler_type::op_extract_external_block, sbid, time_count));
00831         return external_block_type();
00832     }
00833 
00834     virtual void swappable_blocks_resize(swappable_block_identifier_type size)
00835     {
00836         reference_counts.resize(size, 0);
00837     }
00838 
00839     virtual bool is_initialized(const swappable_block_identifier_type sbid) const
00840     {
00841         return reference_counts[sbid] > 0;
00842     }
00843 
00844     virtual void explicit_timestep()
00845     { ++time_count; };
00846 
00847     virtual bool is_simulating() const
00848     { return true; }
00849 };
00850 
00851 //! \brief Block scheduling algorithm caching via the longest forward distance policy (offline).
00852 template <class SwappableBlockType>
00853 class block_scheduler_algorithm_offline_lfd : public block_scheduler_algorithm<SwappableBlockType>
00854 {
00855 protected:
00856     typedef block_scheduler<SwappableBlockType> block_scheduler_type;
00857     typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type;
00858     typedef typename block_scheduler_type::internal_block_type internal_block_type;
00859     typedef typename block_scheduler_type::external_block_type external_block_type;
00860     typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type;
00861     typedef typename block_scheduler_algorithm_type::time_type time_type;
00862     typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type;
00863 
00864     using block_scheduler_algorithm_type::bs;
00865     using block_scheduler_algorithm_type::swappable_blocks;
00866     using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler;
00867     using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler;
00868     using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler;
00869 
00870     class priority
00871     {
00872         unsigned_type p;
00873 
00874     public:
00875         priority(const SwappableBlockType & sblock, const std::pair<bool, time_type> & t)
00876         {
00877             // p larger => evict earlier
00878             if (t.first)
00879             {
00880                 // most significant: next use
00881                 p = unsigned_type(t.second) << 2;
00882                 // less significant: not dirty
00883                 p |= unsigned_type(! sblock.is_dirty()) << 1;
00884                 // less significant: has external_block
00885                 p |= unsigned_type(sblock.has_external_block()) << 0;
00886             }
00887             else
00888             {
00889                 // most significant: next use
00890                 p = std::numeric_limits<unsigned_type>::max() << 2;
00891                 // less significant: next operation: extract > accessed no more > deinitialize
00892                 p |= unsigned_type(t.second) << 0;
00893             }
00894         }
00895 
00896         // less => evict earlier
00897         bool operator < (const priority & right) const
00898         { return p > right.p; }
00899     };
00900 
00901     //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired.
00902     addressable_priority_queue<swappable_block_identifier_type, priority> evictable_blocks;
00903     /*! \brief stores for the sequence of releases extracted from the prediction_sequence:
00904             (true, timestamp of the blocks next acquire) if it is acquired next
00905             (false, 0) if it is deinitialized next
00906             (false, 1) if it is not accessed any more
00907             (false, 2) if it is extracted next
00908             (false, 3) if it is initialized next */
00909     std::deque< std::pair<bool, time_type> > next_use;
00910 
00911     internal_block_type * get_free_internal_block()
00912     {
00913         // try to get a free internal_block
00914         if (internal_block_type * iblock = get_free_internal_block_from_block_scheduler())
00915             return iblock;
00916         // evict block
00917         assert(! evictable_blocks.empty()); // fails it there is not enough memory available
00918         return swappable_blocks[evictable_blocks.pop()].detach_internal_block();
00919     }
00920 
00921     void return_free_internal_block(internal_block_type * iblock)
00922     { return_free_internal_block_to_block_scheduler(iblock); }
00923 
00924     void init(block_scheduler_algorithm_type * old_algo)
00925     {
00926         std::vector< std::pair<bool, time_type> >
00927                 blocks_next_acquire(swappable_blocks.size(), std::make_pair(false, 1));
00928         if(old_algo)
00929         {
00930             // precomputations for priorities: init next_acquires
00931             const prediction_sequence_type & ps = old_algo->get_prediction_sequence();
00932             for (typename prediction_sequence_type::const_reverse_iterator it = ps.rbegin(); it != ps.rend(); ++it)
00933                 switch (it->op)
00934                 {
00935                 case (block_scheduler_type::op_acquire):
00936                 case (block_scheduler_type::op_acquire_uninitialized):
00937                     blocks_next_acquire[it->id] = std::make_pair(true, it->time);
00938                     break;
00939                 case (block_scheduler_type::op_release):
00940                 case (block_scheduler_type::op_release_dirty):
00941                     next_use.push_front(blocks_next_acquire[it->id]);
00942                     break;
00943                 case (block_scheduler_type::op_deinitialize):
00944                     blocks_next_acquire[it->id] = std::make_pair(false, 0);
00945                     break;
00946                 case (block_scheduler_type::op_initialize):
00947                     blocks_next_acquire[it->id] = std::make_pair(false, 3);
00948                     break;
00949                 case (block_scheduler_type::op_extract_external_block):
00950                     blocks_next_acquire[it->id] = std::make_pair(false, 2);
00951                     break;
00952                 }
00953         }
00954         if (get_algorithm_from_block_scheduler())
00955         {
00956             while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty())
00957             {
00958                 // insert already evictable blocks with the right priority
00959                 const swappable_block_identifier_type sbid = get_algorithm_from_block_scheduler()->evictable_blocks_pop();
00960                 evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], blocks_next_acquire[sbid]));
00961             }
00962         }
00963     }
00964 
00965 public:
00966     block_scheduler_algorithm_offline_lfd(block_scheduler_type & bs)
00967         : block_scheduler_algorithm_type(bs)
00968     { init(get_algorithm_from_block_scheduler()); }
00969 
00970     // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence
00971     block_scheduler_algorithm_offline_lfd(block_scheduler_algorithm_type * old)
00972         : block_scheduler_algorithm_type(old)
00973     { init(old); }
00974 
00975     virtual ~block_scheduler_algorithm_offline_lfd()
00976     {
00977         if (! evictable_blocks.empty())
00978             STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lfd that still holds evictable blocks. They get deinitialized.");
00979         while (! evictable_blocks.empty())
00980         {
00981             SwappableBlockType & sblock = swappable_blocks[evictable_blocks.pop()];
00982             if (internal_block_type * iblock = sblock.deinitialize())
00983                 return_free_internal_block(iblock);
00984         }
00985     }
00986 
00987     virtual bool evictable_blocks_empty()
00988     { return evictable_blocks.empty(); }
00989 
00990     virtual swappable_block_identifier_type evictable_blocks_pop()
00991     { return evictable_blocks.pop(); }
00992 
00993     virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false)
00994     {
00995         SwappableBlockType & sblock = swappable_blocks[sbid];
00996         /* acquired => internal -> increase reference count
00997            internal but not acquired -> remove from evictable_blocks, increase reference count
00998            not intern => uninitialized or external -> get internal_block, increase reference count
00999            uninitialized -> fill with default value
01000            external -> read */
01001         if (sblock.is_internal())
01002         {
01003             if (! sblock.is_acquired())
01004                 // not acquired yet -> remove from evictable_blocks
01005                 evictable_blocks.erase(sbid);
01006             sblock.acquire();
01007         }
01008         else if (sblock.is_initialized())
01009         {
01010             // => external but not internal
01011             //get internal_block
01012             sblock.attach_internal_block(get_free_internal_block());
01013             if (! uninitialized)
01014                 //load block synchronously
01015                 sblock.read_sync();
01016             sblock.acquire();
01017         }
01018         else
01019         {
01020             // => ! sblock.is_initialized()
01021             //get internal_block
01022             sblock.attach_internal_block(get_free_internal_block());
01023             sblock.acquire();
01024             //initialize new block
01025             if (! uninitialized)
01026                 sblock.fill_default();
01027         }
01028         return sblock.get_internal_block();
01029     }
01030 
01031     virtual void release(swappable_block_identifier_type sbid, const bool dirty)
01032     {
01033         if (next_use.empty())
01034         {
01035             STXXL_ERRMSG("block_scheduler_algorithm_offline_lfd got release-request but prediction sequence ended. Switching to block_scheduler_algorithm_online.");
01036             // switch algorithm
01037             block_scheduler_algorithm_type * new_algo,
01038                                            * old_algo;
01039             new_algo = new block_scheduler_algorithm_online_lru<SwappableBlockType>(bs);
01040             old_algo = bs.switch_algorithm_to(new_algo);
01041             // redirect call
01042             new_algo->release(sbid, dirty);
01043             // delete self
01044             delete old_algo;
01045             return;
01046         }
01047         SwappableBlockType & sblock = swappable_blocks[sbid];
01048         sblock.make_dirty_if(dirty);
01049         sblock.release();
01050         if (! sblock.is_acquired())
01051         {
01052             if (sblock.is_dirty() || sblock.is_external())
01053                 // => evictable, put in pq
01054                 evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], next_use.front()));
01055             else
01056                 // => uninitialized, release internal block and put it in freelist
01057                 return_free_internal_block(sblock.detach_internal_block());
01058         }
01059         next_use.pop_front();
01060     }
01061 
01062     virtual void deinitialize(swappable_block_identifier_type sbid)
01063     {
01064         SwappableBlockType & sblock = swappable_blocks[sbid];
01065         if (sblock.is_evictable())
01066             evictable_blocks.erase(sbid);
01067         if (internal_block_type * iblock = sblock.deinitialize())
01068             return_free_internal_block(iblock);
01069     }
01070 
01071     virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock)
01072     {
01073         SwappableBlockType & sblock = swappable_blocks[sbid];
01074         sblock.initialize(eblock);
01075     }
01076 
01077     virtual external_block_type extract_external_block(swappable_block_identifier_type sbid)
01078     {
01079         SwappableBlockType & sblock = swappable_blocks[sbid];
01080         if (sblock.is_evictable())
01081             evictable_blocks.erase(sbid);
01082         if (sblock.is_internal())
01083             return_free_internal_block(sblock.detach_internal_block());
01084         return sblock.extract_external_block();
01085     }
01086 };
01087 
01088 //! \brief Block scheduling algorithm caching via the least recently used policy (offline),
01089 //! and prefetching in addition.
01090 template <class SwappableBlockType>
01091 class block_scheduler_algorithm_offline_lru_prefetching : public block_scheduler_algorithm<SwappableBlockType>
01092 {
01093 protected:
01094     struct scheduled_block_meta;
01095     struct write_read_request;
01096 
01097     typedef block_scheduler<SwappableBlockType> block_scheduler_type;
01098     typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type;
01099     typedef typename block_scheduler_type::internal_block_type internal_block_type;
01100     typedef typename block_scheduler_type::external_block_type external_block_type;
01101     typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type;
01102     typedef typename block_scheduler_algorithm_type::time_type time_type;
01103     typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type;
01104     typedef typename block_scheduler_type::block_scheduler_operation block_scheduler_operation;
01105     typedef typename std::vector<SwappableBlockType>::iterator swappable_blocks_iterator;
01106 
01107     typedef std::map<swappable_block_identifier_type, scheduled_block_meta> scheduled_blocks_type;
01108     typedef typename scheduled_blocks_type::iterator scheduled_blocks_iterator;
01109     typedef typename scheduled_blocks_type::reference scheduled_blocks_reference;
01110     typedef std::map<swappable_block_identifier_type, write_read_request *> write_scheduled_blocks_type;
01111     typedef typename write_scheduled_blocks_type::iterator write_scheduled_blocks_iterator;
01112     typedef typename write_scheduled_blocks_type::reference write_scheduled_blocks_reference;
01113 
01114     using block_scheduler_algorithm_type::bs;
01115     using block_scheduler_algorithm_type::swappable_blocks;
01116     using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler;
01117     using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler;
01118     using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler;
01119     using block_scheduler_algorithm_type::prediction_sequence;
01120 
01121     struct write_read_request
01122     {
01123         bool write_done_soon; // set by read_after_write, checked by schedule_read()
01124         bool shall_read; // checked by read_after_write, set by schedule_read()
01125         swappable_blocks_iterator block_to_start_read; // used by read_after_write, set by schedule_read()
01126         scheduled_blocks_iterator taker; // read_req set by read_after_write
01127         request_ptr write_req; // completes with read_after_write
01128 
01129         write_read_request()
01130             : write_done_soon(false), shall_read(false), block_to_start_read(), taker(), write_req(0) {}
01131     };
01132 
01133     struct read_after_write
01134     {
01135         write_read_request * wrr;
01136 
01137         read_after_write(write_read_request * write_read_req)
01138             : wrr(write_read_req) {}
01139 
01140         void operator () (request *)
01141         {
01142             wrr->write_done_soon = true;
01143             if (wrr->shall_read)
01144                 wrr->taker->second.read_req = wrr->block_to_start_read->read_async();
01145         }
01146     };
01147 
01148     struct scheduled_block_meta
01149     {
01150         internal_block_type * reserved_iblock;
01151         std::pair<bool, swappable_block_identifier_type> giver;
01152         request_ptr read_req;
01153         std::deque<block_scheduler_operation> operations; // invariant: not empty; front: last scheduled operation, back: upcoming operation
01154 
01155         scheduled_block_meta(block_scheduler_operation op)
01156             : reserved_iblock(0),
01157               giver(false, 0),
01158               read_req(0),
01159               operations()
01160         { operations.push_front(op); }
01161     };
01162 
01163     //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired.
01164     std::set<swappable_block_identifier_type> free_evictable_blocks;
01165     std::set<swappable_block_identifier_type> scheduled_evictable_blocks;
01166     //! \brief Holds not internal swappable_blocks, whose next access has already been scheduled.
01167     scheduled_blocks_type scheduled_blocks;
01168     //! \brief Holds swappable_blocks, whose internal block has been taken away but the clean did not finish yet.
01169     write_scheduled_blocks_type write_scheduled_blocks;
01170     typename prediction_sequence_type::iterator next_op_to_schedule;
01171 
01172     //! \brief Schedule an internal, possibly dirty swappable_block to write.
01173     //!
01174     //! The block becomes not dirty. if it was dirty, an entry in write_scheduled_blocks is made referencing the write_read_request.
01175     //! \param sbid block to write
01176     //! \return pointer to the write_read_request
01177     write_read_request * schedule_write(const swappable_block_identifier_type sbid)
01178     {
01179         SwappableBlockType & sblock = swappable_blocks[sbid];
01180         write_read_request * wrr = new write_read_request;
01181         wrr->write_req = sblock.clean_async(read_after_write(wrr));
01182         if (wrr->write_req.valid())
01183         {
01184             bool t = write_scheduled_blocks.insert(std::make_pair(sbid, wrr)).second;
01185             assert(t);
01186             return wrr;
01187         }
01188         else
01189         {
01190             delete wrr;
01191             return 0;
01192         }
01193     }
01194 
01195     //! \brief try to interrupt a read scheduled in a write_read_request
01196     //!
01197     //! side-effect: possibly erases entry from write_scheduled_blocks, so the iterator writing_block may become invalid
01198     //! \return if successful
01199     bool try_interrupt_read(const write_scheduled_blocks_iterator & writing_block)
01200     {
01201         // stop read
01202         writing_block->second->shall_read = false;
01203         // check if stopped
01204         if (! writing_block->second->write_done_soon)
01205             return true;
01206         // => possibly to late
01207         scheduled_blocks_reference taker = *writing_block->second->taker;
01208         // wait
01209         wait_on_write(writing_block);
01210         // check if read started
01211         if (taker.second.read_req.valid())
01212             // => read started, to late
01213             return false;
01214         else
01215             // => just in time
01216             return true;
01217     }
01218 
01219     //! \brief Schedule an internal and external block to read.
01220     //!
01221     //! If the giver is still writing, schedule read via its write_read_request.
01222     void schedule_read(scheduled_blocks_iterator block_to_read)
01223     {
01224         // first check if block_to_read is still writing. do not read before write finished
01225         // wait_on_write(block_to_read->first);
01226 
01227         write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->first);
01228         if (it != write_scheduled_blocks.end())
01229         {
01230             scheduled_blocks_iterator other_block_to_read = it->second->taker;
01231             // check if scheduled to read
01232             if (it->second->shall_read)
01233             {
01234                 if (try_interrupt_read(it))
01235                 {
01236                     // => interrupted, swap internal_blocks
01237                     std::swap(other_block_to_read->second.giver, block_to_read->second.giver);
01238                     if (other_block_to_read->second.giver.first)
01239                     {
01240                         write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second);
01241                         if (it != write_scheduled_blocks.end())
01242                             it->second->taker = other_block_to_read;
01243                         else
01244                             other_block_to_read->second.giver.first = false;
01245                     }
01246                     if (block_to_read->second.giver.first)
01247                     {
01248                         write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second);
01249                         if (it != write_scheduled_blocks.end())
01250                             it->second->taker = block_to_read;
01251                         else
01252                             block_to_read->second.giver.first = false;
01253                     }
01254 
01255                     internal_block_type * tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block();
01256                     swappable_blocks[block_to_read->first].attach_internal_block(
01257                             swappable_blocks[other_block_to_read->first].detach_internal_block());
01258                     swappable_blocks[other_block_to_read->first].attach_internal_block(tmp_iblock);
01259                     // => this block has its internal_block back, no need to read
01260                     // reschedule other
01261                     schedule_read(other_block_to_read);
01262                     return;
01263                 }
01264                 // else => read already started, but write done -> read this
01265             }
01266             else
01267             {
01268                 // => no read scheduled, swap internal_blocks
01269                 std::swap(other_block_to_read->second.giver, block_to_read->second.giver);
01270                 if (other_block_to_read->second.giver.first)
01271                 {
01272                     write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second);
01273                     if (it != write_scheduled_blocks.end())
01274                         it->second->taker = other_block_to_read;
01275                     else
01276                         other_block_to_read->second.giver.first = false;
01277                 }
01278                 if (block_to_read->second.giver.first)
01279                 {
01280                     write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second);
01281                     if (it != write_scheduled_blocks.end())
01282                         it->second->taker = block_to_read;
01283                     else
01284                         block_to_read->second.giver.first = false;
01285                 }
01286 
01287                 internal_block_type * tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block();
01288                 swappable_blocks[block_to_read->first].attach_internal_block(
01289                         other_block_to_read->second.reserved_iblock);
01290                 other_block_to_read->second.reserved_iblock = tmp_iblock;
01291                 // => this block has its internal_block back, no need to read
01292                 return;
01293             }
01294         }
01295 
01296         // schedule block_to_read to read
01297         if (block_to_read->second.giver.first)
01298         {
01299             write_scheduled_blocks_iterator writing_block = write_scheduled_blocks.find(block_to_read->second.giver.second);
01300             if (writing_block != write_scheduled_blocks.end())
01301             {
01302                 // => there is a write scheduled
01303                 // tell the completion handler that we want a read
01304                 writing_block->second->block_to_start_read = swappable_blocks.begin() + block_to_read->first;
01305                 writing_block->second->taker = block_to_read;
01306                 writing_block->second->shall_read = true;
01307                 // and check if it is not to late
01308                 if (writing_block->second->write_done_soon)
01309                 {
01310                     // => the completion handler may have missed our wish to read
01311                     // so wait for it to finish and check
01312                     wait_on_write(writing_block);
01313                     block_to_read->second.giver.first = false;
01314                     if (block_to_read->second.read_req.valid())
01315                         // read scheduled
01316                         return;
01317                 }
01318                 else
01319                     // read scheduled
01320                     return;
01321             }
01322             else
01323                 block_to_read->second.giver.first = false;
01324         }
01325         // => read could not be scheduled through the completion handler
01326         block_to_read->second.read_req = swappable_blocks[block_to_read->first].read_async();
01327     }
01328 
01329     //! \brief wait for the write to finish
01330     //!
01331     //! side-effect: erases entry from write_scheduled_blocks
01332     void wait_on_write(const write_scheduled_blocks_iterator & writing_block)
01333     {
01334         writing_block->second->write_req->wait();
01335         delete writing_block->second;
01336         write_scheduled_blocks.erase(writing_block);
01337     }
01338 
01339     //! \brief wait for the write to finish
01340     //!
01341     //! side-effect: erases entry from write_scheduled_blocks
01342     void wait_on_write(const swappable_block_identifier_type & writing_block)
01343     {
01344         write_scheduled_blocks_iterator it = write_scheduled_blocks.find(writing_block);
01345         if (it != write_scheduled_blocks.end())
01346             wait_on_write(it);
01347     }
01348 
01349     //! \brief wait for the write of the giver to finish
01350     //!
01351     //! side-effect: erases entry from write_scheduled_blocks
01352     void wait_on_write(const scheduled_blocks_iterator & schedule_meta)
01353     {
01354         if (schedule_meta->second.giver.first)
01355         {
01356             wait_on_write(schedule_meta->second.giver.second);
01357             schedule_meta->second.giver.first = false;
01358         }
01359     }
01360 
01361     //! \brief wait for the read to finish
01362     //!
01363     //! side-effect: erases entry for the write of the giver from write_scheduled_blocks
01364     void wait_on_read(const scheduled_blocks_iterator & schedule_meta)
01365     {
01366         wait_on_write(schedule_meta);
01367         if (schedule_meta->second.read_req.valid())
01368         {
01369             schedule_meta->second.read_req->wait();
01370             schedule_meta->second.read_req = 0;
01371         }
01372     }
01373 
01374     //! \brief wait for the write of the giver to finish and return reserved internal_block
01375     //!
01376     //! side-effect: erases entry for the write of the giver from write_scheduled_blocks
01377     internal_block_type * get_ready_block(const scheduled_blocks_iterator & schedule_meta)
01378     {
01379         wait_on_write(schedule_meta);
01380         internal_block_type * r = schedule_meta->second.reserved_iblock;
01381         schedule_meta->second.reserved_iblock = 0;
01382         return r;
01383     }
01384 
01385     bool shall_keep_internal_block(const scheduled_blocks_iterator & schedule_meta, const bool ignore_first = true) const
01386     {
01387         // returns true iif there is an acquire or acquire_uninitialized scheduled or there is a deinitialize scheduled and the block is dirty
01388         for (typename std::deque<block_scheduler_operation>::reverse_iterator
01389                 rit = schedule_meta->second.operations.rbegin() + ignore_first;
01390                 rit != schedule_meta->second.operations.rend(); ++rit)
01391             switch (*rit)
01392             {
01393             case block_scheduler_type::op_acquire:
01394             case block_scheduler_type::op_acquire_uninitialized:
01395                 return true; break;
01396             case block_scheduler_type::op_release:
01397             case block_scheduler_type::op_release_dirty:
01398                 break;
01399             case block_scheduler_type::op_deinitialize:
01400                 if (swappable_blocks[schedule_meta->first].is_dirty()) return true; break;
01401             case block_scheduler_type::op_initialize:
01402             case block_scheduler_type::op_extract_external_block:
01403                 break;
01404             }
01405         return false;
01406     }
01407 
01408     // assumes the current operation to be still in operations
01409     bool shall_be_cleaned(const scheduled_blocks_iterator & schedule_meta) const
01410     {
01411         // returns true iif there is an extract_external_block scheduled and no release_dirty, deinitialize or initialize before
01412         for (typename std::deque<block_scheduler_operation>::reverse_iterator
01413                 rit = schedule_meta->second.operations.rbegin() + 1;
01414                 rit != schedule_meta->second.operations.rend(); ++rit)
01415             switch (*rit)
01416             {
01417             case block_scheduler_type::op_acquire:
01418             case block_scheduler_type::op_acquire_uninitialized:
01419             case block_scheduler_type::op_release:
01420                 break;
01421             case block_scheduler_type::op_release_dirty:
01422             case block_scheduler_type::op_deinitialize:
01423             case block_scheduler_type::op_initialize:
01424                 return false; break;
01425             case block_scheduler_type::op_extract_external_block:
01426                 return true; break;
01427             }
01428         return false;
01429     }
01430 
01431     bool shall_be_read(const scheduled_blocks_iterator & schedule_meta, const bool ignore_first = true) const
01432     {
01433         // returns true iif there is an acquire scheduled next and the block is initialized
01434         return swappable_blocks[schedule_meta->first].is_initialized()
01435                 && schedule_meta->second.operations.rbegin() + ignore_first != schedule_meta->second.operations.rend()
01436                 && *(schedule_meta->second.operations.rbegin() + ignore_first) == block_scheduler_type::op_acquire;
01437     }
01438 
01439     void operation_done(scheduled_blocks_iterator & schedule_meta)
01440     {
01441         schedule_meta->second.operations.pop_back();
01442         if (schedule_meta->second.operations.empty())
01443         {
01444             assert(! schedule_meta->second.giver.first);
01445             scheduled_blocks.erase(schedule_meta);
01446         }
01447     }
01448 
01449     block_scheduler_algorithm_type * give_up(std::string err_msg = "detected some error in the prediction sequence")
01450     {
01451         STXXL_ERRMSG("block_scheduler_algorithm_offline_lru_prefetching: " << err_msg << ". Switching to block_scheduler_algorithm_online.");
01452         // switch algorithm
01453         block_scheduler_algorithm_type * new_algo
01454                 = new block_scheduler_algorithm_online_lru<SwappableBlockType>(bs);
01455         // and delete self
01456         delete bs.switch_algorithm_to(new_algo);
01457         return new_algo;
01458     }
01459 
01460     void return_free_internal_block(internal_block_type * iblock)
01461     { return_free_internal_block_to_block_scheduler(iblock); }
01462 
01463     void schedule_next_operations()
01464     {
01465         while (next_op_to_schedule != prediction_sequence.end())
01466         {
01467             // list operation in scheduled_blocks
01468             std::pair<scheduled_blocks_iterator, bool> ins_res = scheduled_blocks.insert(
01469                     std::make_pair(next_op_to_schedule->id, next_op_to_schedule->op));
01470             scheduled_blocks_iterator schedule_meta = ins_res.first;
01471             if (! ins_res.second)
01472                 schedule_meta->second.operations.push_front(next_op_to_schedule->op);
01473             SwappableBlockType & sblock = swappable_blocks[next_op_to_schedule->id];
01474 
01475             // do appropriate preparations
01476             if (next_op_to_schedule->op == block_scheduler_type::op_acquire
01477                     || next_op_to_schedule->op == block_scheduler_type::op_acquire_uninitialized)
01478             {
01479                 if (sblock.is_internal())
01480                 {
01481                     if (free_evictable_blocks.erase(next_op_to_schedule->id))
01482                         scheduled_evictable_blocks.insert(next_op_to_schedule->id);
01483                 }
01484                 else
01485                     if (! schedule_meta->second.reserved_iblock)
01486                     {
01487                         // => needs internal_block -> try to get one
01488                         // -> try to get one from block_scheduler
01489                         if (! (schedule_meta->second.reserved_iblock = get_free_internal_block_from_block_scheduler()))
01490                         {
01491                             // -> try to get one by evicting
01492                             if (free_evictable_blocks.empty())
01493                             {
01494                                 // => can not schedule acquire
01495                                 // remove operation from scheduled_blocks
01496                                 if (ins_res.second)
01497                                     scheduled_blocks.erase(ins_res.first);
01498                                 else
01499                                     schedule_meta->second.operations.pop_front();
01500                                 // stop scheduling
01501                                 return;
01502                             }
01503                             swappable_block_identifier_type giver = pop_begin(free_evictable_blocks);
01504                             {
01505                                 scheduled_blocks_iterator giver_meta;
01506                                 assert((giver_meta = scheduled_blocks.find(giver)) == scheduled_blocks.end()
01507                                         || ! shall_keep_internal_block(giver_meta, false));
01508                             }
01509                             write_read_request * wrr = schedule_write(giver);
01510                             schedule_meta->second.giver.first = bool(wrr);
01511                             schedule_meta->second.giver.second = giver;
01512                             schedule_meta->second.reserved_iblock = swappable_blocks[giver].detach_internal_block();
01513                             if (wrr)
01514                                 wrr->taker = schedule_meta;
01515                         }
01516                         // read if desired
01517                         if (shall_be_read(schedule_meta, false))
01518                         {
01519                             // => there is no operation scheduled for this block before this acquire and it is initialized
01520                             // -> start prefetching now
01521                             sblock.attach_internal_block(schedule_meta->second.reserved_iblock);
01522                             schedule_meta->second.reserved_iblock = 0;
01523                             scheduled_evictable_blocks.insert(next_op_to_schedule->id);
01524                             schedule_read(schedule_meta);
01525                         }
01526                     }
01527             }
01528             else if (next_op_to_schedule->op == block_scheduler_type::op_deinitialize)
01529             {
01530                 if (sblock.is_dirty())
01531                     if (free_evictable_blocks.erase(next_op_to_schedule->id))
01532                         scheduled_evictable_blocks.insert(next_op_to_schedule->id);
01533             }
01534 
01535             ++ next_op_to_schedule;
01536         }
01537         for (typename std::set<swappable_block_identifier_type>::iterator it = free_evictable_blocks.begin();
01538                 it != free_evictable_blocks.end(); ++it)
01539             if (! write_scheduled_blocks.count(*it))
01540                 schedule_write(*it);
01541     }
01542 
01543     void init(block_scheduler_algorithm_type * old_algo)
01544     {
01545         if(old_algo)
01546             // copy prediction sequence
01547             prediction_sequence = old_algo->get_prediction_sequence();
01548         next_op_to_schedule = prediction_sequence.begin();
01549         if (get_algorithm_from_block_scheduler())
01550             while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty())
01551                 free_evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop());
01552         schedule_next_operations();
01553     }
01554 
01555     void deinit()
01556     {
01557         // todo remove
01558         if (! scheduled_blocks.empty())
01559             STXXL_MSG("deinit while scheduled_blocks not empty");
01560         if (! scheduled_evictable_blocks.empty())
01561             STXXL_MSG("deinit while scheduled_evictable_blocks not empty");
01562 
01563         // empty scheduled_blocks
01564         free_evictable_blocks.insert(scheduled_evictable_blocks.begin(), scheduled_evictable_blocks.end());
01565         //for (typename std::set<swappable_block_identifier_type>::iterator it = scheduled_evictable_blocks.begin();
01566         //        it != scheduled_evictable_blocks.end(); ++it)
01567         //    free_evictable_blocks.insert(*it);
01568         scheduled_evictable_blocks.clear();
01569         while (! scheduled_blocks.empty())
01570         {
01571             scheduled_blocks_iterator it = scheduled_blocks.begin();
01572             wait_on_read(it);
01573             if (it->second.reserved_iblock)
01574                 return_free_internal_block(it->second.reserved_iblock);
01575             scheduled_blocks.erase(it);
01576         }
01577         while (! write_scheduled_blocks.empty())
01578         {
01579             write_scheduled_blocks_iterator it = write_scheduled_blocks.begin();
01580             wait_on_write(it);
01581         }
01582     }
01583 
01584 public:
01585     block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_type & bs)
01586         : block_scheduler_algorithm_type(bs)
01587     { init(get_algorithm_from_block_scheduler()); }
01588 
01589     // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence
01590     block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_algorithm_type * old)
01591         : block_scheduler_algorithm_type(old)
01592     { init(old); }
01593 
01594     virtual ~block_scheduler_algorithm_offline_lru_prefetching()
01595     {
01596         deinit();
01597         if (! free_evictable_blocks.empty())
01598             STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lru_prefetching that still holds evictable blocks. They get deinitialized.");
01599         while (! free_evictable_blocks.empty())
01600         {
01601             SwappableBlockType & sblock = swappable_blocks[pop_begin(free_evictable_blocks)];
01602             if (internal_block_type * iblock = sblock.deinitialize())
01603                 return_free_internal_block(iblock);
01604         }
01605     }
01606 
01607     virtual bool evictable_blocks_empty()
01608     {
01609         deinit();
01610         return free_evictable_blocks.empty();
01611     }
01612 
01613     virtual swappable_block_identifier_type evictable_blocks_pop()
01614     { return pop_begin(free_evictable_blocks); }
01615 
01616     virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false)
01617     {
01618         assert(! prediction_sequence.empty());
01619         assert(prediction_sequence.front().op ==
01620                 ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire));
01621         assert(prediction_sequence.front().id == sbid);
01622         prediction_sequence.pop_front();
01623         scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid);
01624         assert(schedule_meta != scheduled_blocks.end()); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory)
01625         assert(schedule_meta->second.operations.back() ==
01626                         ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory)
01627 
01628         SwappableBlockType & sblock = swappable_blocks[sbid];
01629         /* acquired => internal -> increase reference count
01630            internal but not acquired -> remove from scheduled_evictable_blocks, increase reference count
01631            not internal => uninitialized or external -> get internal_block, increase reference count
01632            uninitialized -> fill with default value
01633            external -> read */
01634         if (sblock.is_internal())
01635         {
01636             if (! sblock.is_acquired())
01637             {
01638                 // not acquired yet -> remove from scheduled_evictable_blocks
01639                 bool t = scheduled_evictable_blocks.erase(sbid);
01640                 assert(t);
01641                 wait_on_read(schedule_meta);
01642             }
01643             sblock.acquire();
01644         }
01645         else
01646         {
01647             assert(uninitialized || ! sblock.is_initialized()); // initialized blocks should be scheduled to read and thus internal
01648             //get internal_block
01649             sblock.attach_internal_block(get_ready_block(schedule_meta));
01650             sblock.acquire();
01651             //initialize new block
01652             if (! uninitialized)
01653                 sblock.fill_default();
01654         }
01655 
01656         operation_done(schedule_meta);
01657         return sblock.get_internal_block();
01658     }
01659 
01660     virtual void release(swappable_block_identifier_type sbid, const bool dirty)
01661     {
01662         assert(! prediction_sequence.empty());
01663         assert(prediction_sequence.front().op ==
01664                 ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release));
01665         assert(prediction_sequence.front().id == sbid);
01666         prediction_sequence.pop_front();
01667         scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid);
01668         assert(schedule_meta != scheduled_blocks.end());
01669         assert(schedule_meta->second.operations.back() ==
01670                         ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release));
01671 
01672         SwappableBlockType & sblock = swappable_blocks[sbid];
01673         sblock.make_dirty_if(dirty);
01674         sblock.release();
01675         if (! sblock.is_acquired())
01676         {
01677             if (sblock.is_dirty() || sblock.is_external())
01678             {
01679                 // => evictable
01680                 if (shall_keep_internal_block(schedule_meta))
01681                 {
01682                     // => swappable_block shall keep its internal_block
01683                     scheduled_evictable_blocks.insert(sbid);
01684                     if (shall_be_cleaned(schedule_meta))
01685                         schedule_write(sbid);
01686                 }
01687                 else
01688                 {
01689                     // give block to scheduler
01690                     free_evictable_blocks.insert(sbid);
01691                     if (next_op_to_schedule != prediction_sequence.end())
01692                         schedule_next_operations();
01693                     else
01694                         if (! write_scheduled_blocks.count(sbid))
01695                             schedule_write(sbid);
01696                 }
01697             }
01698             else
01699             {
01700                 // => uninitialized
01701                 if (shall_keep_internal_block(schedule_meta))
01702                     // => swappable_block shall keep its internal_block
01703                     schedule_meta->second.reserved_iblock = sblock.detach_internal_block();
01704                 else
01705                 {
01706                     // release internal block and give it to prefetcher
01707                     return_free_internal_block(sblock.detach_internal_block());
01708                     if (next_op_to_schedule != prediction_sequence.end())
01709                         schedule_next_operations();
01710                 }
01711             }
01712         }
01713         operation_done(schedule_meta);
01714     }
01715 
01716     virtual void deinitialize(swappable_block_identifier_type sbid)
01717     {
01718         assert(! prediction_sequence.empty());
01719         assert(prediction_sequence.front().op == block_scheduler_type::op_deinitialize);
01720         assert(prediction_sequence.front().id == sbid);
01721         prediction_sequence.pop_front();
01722         scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid);
01723         assert(schedule_meta != scheduled_blocks.end());
01724         assert(schedule_meta->second.operations.back() == block_scheduler_type::op_deinitialize);
01725 
01726         SwappableBlockType & sblock = swappable_blocks[sbid];
01727         if (sblock.is_evictable())
01728         {
01729             bool t;
01730             if (shall_keep_internal_block(schedule_meta, false))
01731             {
01732                 if (! (t = scheduled_evictable_blocks.erase(sbid)))
01733                 {
01734                     STXXL_ERRMSG("dirty block not scheduled on deinitialize");
01735                     t = free_evictable_blocks.erase(sbid);
01736                 }
01737             }
01738             else
01739                 t = free_evictable_blocks.erase(sbid);
01740             assert(t);
01741         }
01742         if (internal_block_type * iblock = sblock.deinitialize())
01743         {
01744             if (shall_keep_internal_block(schedule_meta))
01745                 // => swappable_block shall keep its internal_block
01746                 schedule_meta->second.reserved_iblock = iblock;
01747             else
01748             {
01749                 // release internal block and give it to prefetcher
01750                 return_free_internal_block(iblock);
01751                 if (next_op_to_schedule != prediction_sequence.end())
01752                     schedule_next_operations();
01753             }
01754         }
01755         operation_done(schedule_meta);
01756     }
01757 
01758     virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock)
01759     {
01760         assert(! prediction_sequence.empty());
01761         assert(prediction_sequence.front().op == block_scheduler_type::op_initialize);
01762         assert(prediction_sequence.front().id == sbid);
01763         prediction_sequence.pop_front();
01764         scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid);
01765         assert(schedule_meta != scheduled_blocks.end());
01766         assert(schedule_meta->second.operations.back() == block_scheduler_type::op_initialize);
01767 
01768         SwappableBlockType & sblock = swappable_blocks[sbid];
01769         sblock.initialize(eblock);
01770         if (shall_be_read(schedule_meta))
01771         {
01772             sblock.attach_internal_block(schedule_meta->second.reserved_iblock);
01773             schedule_meta->second.reserved_iblock = 0;
01774             scheduled_evictable_blocks.insert(sbid);
01775             schedule_read(schedule_meta);
01776         }
01777         operation_done(schedule_meta);
01778     }
01779 
01780     virtual external_block_type extract_external_block(swappable_block_identifier_type sbid)
01781     {
01782         assert(! prediction_sequence.empty());
01783         assert(prediction_sequence.front().op == block_scheduler_type::op_extract_external_block);
01784         assert(prediction_sequence.front().id == sbid);
01785         prediction_sequence.pop_front();
01786         scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid);
01787         assert(schedule_meta != scheduled_blocks.end());
01788         assert(schedule_meta->second.operations.back() == block_scheduler_type::op_extract_external_block);
01789 
01790         SwappableBlockType & sblock = swappable_blocks[sbid];
01791         wait_on_write(sbid);
01792         operation_done(schedule_meta);
01793         return sblock.extract_external_block();
01794     }
01795 };
01796 
01797 __STXXL_END_NAMESPACE
01798 
01799 #endif /* STXXL_BLOCK_SCHEDULER_HEADER */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines