00001 // cwchessboard -- A C++ chessboard tool set 00002 // 00003 //! @file MemoryBlockList.cc This file contains the implementation of class MemoryBlockList. 00004 // 00005 // Copyright (C) 2010, by 00006 // 00007 // Carlo Wood, Run on IRC <carlo@alinoe.com> 00008 // RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt 00009 // Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61 00010 // 00011 // This program is free software: you can redistribute it and/or modify 00012 // it under the terms of the GNU General Public License as published by 00013 // the Free Software Foundation, either version 2 of the License, or 00014 // (at your option) any later version. 00015 // 00016 // This program is distributed in the hope that it will be useful, 00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 // GNU General Public License for more details. 00020 // 00021 // You should have received a copy of the GNU General Public License 00022 // along with this program. If not, see <http://www.gnu.org/licenses/>. 00023 00024 #ifndef USE_PCH 00025 #include "sys.h" 00026 #include <glib/gmacros.h> 00027 #endif 00028 00029 #include "MemoryBlockList.h" 00030 00031 namespace util { 00032 00033 // Let the data start at the first multiple of sizeof(size_t) after the MemoryBlockNode object. 00034 size_t const MemoryBlockNode::S_data_offset = (sizeof(MemoryBlockNode) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1); 00035 00036 void MemoryBlockList::need_more_data_callback(void) 00037 { 00038 // Create and read at most one new block per 'buffer full' incident. 00039 // If multiple new blocks have been processed in the mean time, 00040 // then the call to read_async_ready will trigger subsequent reads. 00041 if (!M_buffer_full) 00042 return; 00043 M_buffer_full = false; 00044 M_slot_need_more_data(); 00045 } 00046 00047 void MemoryBlockListIterator::advance_to_next_block(void) 00048 { 00049 // Count the number of fully processed blocks. 00050 ++M_processed_blocks; 00051 Dout(dc::notice, "Finished processing of block " << M_processed_blocks); 00052 if (G_UNLIKELY(M_block->is_last_block())) 00053 { 00054 // We should never even have been processing the last block, 00055 // unless the buffer was already closed. 00056 assert(M_buffer->closed()); 00057 00058 // This might free the memory of this block, if no other iterator is pointing at it. 00059 Dout(dc::notice, "Setting M_block to NULL."); 00060 M_block.reset(); 00061 00062 // We're done. Set the iterator to past-the-end and return. 00063 M_ptr = NULL; 00064 return; 00065 } 00066 else 00067 { 00068 M_block = M_block->next(); 00069 M_ptr = M_block->block_begin(); 00070 M_block_end = M_block->block_end() - 1; 00071 } 00072 if (M_buffer->full() && M_buffer->appended_blocks() - M_processed_blocks <= MemoryBlockList::S_max_blocks / 2) 00073 { 00074 Dout(dc::notice, "Requesting more data!"); 00075 M_buffer->need_more_data().emit(); 00076 } 00077 // If M_buffer->full() was just false but around here the main thread 00078 // sets it to true, then that means that the number of remaining blocks 00079 // in the buffer is much larger than the threshold and the previous 00080 // condition would have failed anyway. What would happen then is that 00081 // the next condition is definitely false (because there ARE blocks to 00082 // be processed), the next block in the buffer will be processed and 00083 // we'll recheck the above condition. Hence, there is no race condition. 00084 while (!M_buffer->can_process_next_block(*this)) 00085 { 00086 // If can_process_next_block returned false (so we get here) 00087 // then the buffer has less than 2 blocks is thus not full (yet). 00088 // If now we manage to get into a sleeping state before the 00089 // buffer is full then everything works. But, if right here 00090 // the buffer is filled -- which causes an attempt to wake 00091 // up this thread, but we're not inside wait() yet... and 00092 // then we enter wait(), then we have a hang. 00093 // Therefore it is needed to lock the mutex then test if 00094 // the buffer is full and only if NOT we may really sleep. 00095 // In the main thread then we may only mark that the buffer is 00096 // full and emit the wake up signal if this mutex is not locked. 00097 // The locking and extra buffer full test is done inside wait_for_more_data(). 00098 00099 // This is triggered when a new block is appended, either when there are at least two blocks, 00100 // or when the last block was appended (the buffer was closed). 00101 M_buffer->wait_for_more_data(*this); 00102 } 00103 } 00104 00105 } // namespace util