| /* |
| Copyright 2005-2010 Intel Corporation. All Rights Reserved. |
| |
| This file is part of Threading Building Blocks. |
| |
| Threading Building Blocks is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License |
| version 2 as published by the Free Software Foundation. |
| |
| Threading Building Blocks is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Threading Building Blocks; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| As a special exception, you may use this file as part of a free software |
| library without restriction. Specifically, if other files instantiate |
| templates or use macros or inline functions from this file, or you compile |
| this file and link it with other files to produce an executable, this |
| file does not by itself cause the resulting executable to be covered by |
| the GNU General Public License. This exception does not however |
| invalidate any other reasons why the executable file might be covered by |
| the GNU General Public License. |
| */ |
| |
| #ifndef _TBB_scheduler_utility_H |
| #define _TBB_scheduler_utility_H |
| |
| #include "scheduler.h" |
| |
| namespace tbb { |
| namespace internal { |
| |
| //------------------------------------------------------------------------ |
| // auto_empty_task |
| //------------------------------------------------------------------------ |
| |
| //! Smart holder for the empty task class with automatic destruction |
| class auto_empty_task { |
| task* my_task; |
| generic_scheduler* my_scheduler; |
| public: |
| auto_empty_task ( __TBB_CONTEXT_ARG(generic_scheduler *s, task_group_context* context) ) |
| : my_task( new(&s->allocate_task(sizeof(empty_task), __TBB_CONTEXT_ARG(NULL, context))) empty_task ) |
| , my_scheduler(s) |
| {} |
| // empty_task has trivial destructor, so there's no need to call it. |
| ~auto_empty_task () { my_scheduler->free_task<small_local_task>(*my_task); } |
| |
| operator task& () { return *my_task; } |
| task* operator & () { return my_task; } |
| task_prefix& prefix () { return my_task->prefix(); } |
| }; // class auto_empty_task |
| |
| |
| //------------------------------------------------------------------------ |
| // Debugging support |
| //------------------------------------------------------------------------ |
| |
| #define __TBB_POISON_DEQUE TBB_USE_ASSERT |
| |
| #if __TBB_POISON_DEQUE |
| #if __TBB_x86_64 || __TBB_ipf |
| static task* const poisoned_taskptr = (task*)0xDDEEAADDDEADBEEF; |
| #else |
| static task* const poisoned_taskptr = (task*)0xDEADBEEF; |
| #endif |
| |
| #define __TBB_POISON_TASK_PTR(ptr) ptr = poisoned_taskptr |
| #define __TBB_ASSERT_VALID_TASK_PTR(ptr) __TBB_ASSERT( ptr != poisoned_taskptr, "task pointer in the deque is poisoned" ) |
| #else /* !__TBB_POISON_DEQUE */ |
| #define __TBB_POISON_TASK_PTR(ptr) ((void)0) |
| #define __TBB_ASSERT_VALID_TASK_PTR(ptr) ((void)0) |
| #endif /* !__TBB_POISON_DEQUE */ |
| |
| //------------------------------------------------------------------------ |
| // fast_reverse_vector |
| //------------------------------------------------------------------------ |
| |
| //! Vector that grows without reallocations, and stores items in the reverse order. |
| /** Requires to initialize its first segment with a preallocated memory chunk |
| (usually it is static array or an array allocated on the stack). |
| The second template parameter specifies maximal number of segments. Each next |
| segment is twice as large as the previous one. **/ |
| template<typename T, size_t max_segments = 16> |
| class fast_reverse_vector |
| { |
| public: |
| fast_reverse_vector ( T* initial_segment, size_t segment_size ) |
| : m_cur_segment(initial_segment) |
| , m_cur_segment_size(segment_size) |
| , m_pos(segment_size) |
| , m_num_segments(0) |
| , m_size(0) |
| { |
| __TBB_ASSERT ( initial_segment && segment_size, "Nonempty initial segment must be supplied"); |
| } |
| |
| ~fast_reverse_vector () |
| { |
| for ( size_t i = 1; i < m_num_segments; ++i ) |
| NFS_Free( m_segments[i] ); |
| } |
| |
| size_t size () const { return m_size + m_cur_segment_size - m_pos; } |
| |
| void push_back ( const T& val ) |
| { |
| if ( !m_pos ) { |
| m_segments[m_num_segments++] = m_cur_segment; |
| __TBB_ASSERT ( m_num_segments < max_segments, "Maximal capacity exceeded" ); |
| m_size += m_cur_segment_size; |
| m_cur_segment_size *= 2; |
| m_pos = m_cur_segment_size; |
| m_cur_segment = (T*)NFS_Allocate( m_cur_segment_size * sizeof(T), 1, NULL ); |
| } |
| m_cur_segment[--m_pos] = val; |
| } |
| |
| //! Copies the contents of the vector into the dst array. |
| /** Can only be used when T is a POD type, as copying does not invoke copy constructors. **/ |
| void copy_memory ( T* dst ) const |
| { |
| size_t size = m_cur_segment_size - m_pos; |
| memcpy( dst, m_cur_segment + m_pos, size * sizeof(T) ); |
| dst += size; |
| size = m_cur_segment_size / 2; |
| for ( long i = (long)m_num_segments - 1; i >= 0; --i ) { |
| memcpy( dst, m_segments[i], size * sizeof(T) ); |
| dst += size; |
| size /= 2; |
| } |
| } |
| |
| protected: |
| //! The current (not completely filled) segment |
| T *m_cur_segment; |
| |
| //! Capacity of m_cur_segment |
| size_t m_cur_segment_size; |
| |
| //! Insertion position in m_cur_segment |
| size_t m_pos; |
| |
| //! Array of filled segments (has fixed size specified by the second template parameter) |
| T *m_segments[max_segments]; |
| |
| //! Number of filled segments (the size of m_segments) |
| size_t m_num_segments; |
| |
| //! Number of items in the segments in m_segments |
| size_t m_size; |
| |
| }; // class fast_reverse_vector |
| |
| |
| //------------------------------------------------------------------------ |
| // Statistics log |
| //------------------------------------------------------------------------ |
| |
| #if STATISTICS |
| //! Class for collecting statistics |
| /** There should be only one instance of this class. |
| Results are written to a file "statistics.txt" in tab-separated format. */ |
| class statistics { |
| public: |
| statistics() { |
| my_file = fopen("statistics.txt","w"); |
| if( !my_file ) { |
| perror("fopen(\"statistics.txt\"\")"); |
| exit(1); |
| } |
| fprintf(my_file,"%13s\t%13s\t%13s\t%13s\t%13s\t%13s\n", "execute", "steal", "mail", "proxy_execute", "proxy_steal", "proxy_bypass" ); |
| } |
| ~statistics() { |
| fclose(my_file); |
| } |
| void record( long execute_count, long steal_count, long mail_received_count, |
| long proxy_execute_count, long proxy_steal_count, long proxy_bypass_count ) { |
| mutex::scoped_lock lock(my_mutex); |
| fprintf (my_file,"%13ld\t%13ld\t%13ld\t%13ld\t%13ld\t%13ld\n", execute_count, steal_count, mail_received_count, |
| proxy_execute_count, proxy_steal_count, proxy_bypass_count ); |
| } |
| private: |
| //! File into which statistics are written. |
| FILE* my_file; |
| //! Mutex that serializes accesses to my_file |
| mutex my_mutex; |
| }; // class statistics |
| #endif /* STATISTICS */ |
| |
| } // namespace internal |
| } // namespace tbb |
| |
| #endif /* _TBB_scheduler_utility_H */ |