| /* |
| 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. |
| */ |
| |
| #include "job_automaton.h" |
| #define HARNESS_NO_PARSE_COMMAND_LINE 1 |
| #include "harness.h" |
| #include "harness_barrier.h" |
| |
| class State { |
| Harness::SpinBarrier barrier; |
| rml::internal::job_automaton ja; |
| rml::job job; |
| tbb::atomic<int> job_created; |
| tbb::atomic<int> job_destroyed; |
| tbb::atomic<bool> job_received; |
| public: |
| State() : barrier(2) { |
| job_created = 0; |
| job_destroyed = 0; |
| job_received = false; |
| } |
| void exercise( bool is_owner ); |
| ~State() { |
| ASSERT( job_created==job_destroyed, "accounting error" ); |
| ASSERT( job_destroyed<=1, "destroyed job twice" ); |
| } |
| }; |
| |
| int DelayMask; |
| const int N = 14; |
| tbb::atomic<int> Coverage[N]; |
| |
| //! Mark kth interval as covered and insert delay if kth bit of DelayMask is set. |
| /** An interval is the code between two operations on the job_automaton that we are testing. */ |
| void Cover( int k ) { |
| ASSERT( k<N, NULL ); |
| ++Coverage[k]; |
| if( DelayMask>>k&1 ) { |
| // Introduce delay (and possibly a thread context switch) |
| __TBB_Yield(); |
| } |
| } |
| |
| void State::exercise( bool is_owner ) { |
| barrier.wait(); |
| if( is_owner ) { |
| Cover(0); |
| if( ja.try_acquire() ) { |
| Cover(1); |
| ++job_created; |
| ja.set_and_release(job); |
| Cover(2); |
| if( ja.try_acquire() ) { |
| Cover(3); |
| ja.release(); |
| Cover(4); |
| if( ja.try_acquire() ) { |
| Cover(5); |
| ja.release(); |
| } |
| } |
| Cover(6); |
| } else { |
| Cover(7); |
| } |
| if( DelayMask&1<<N ) { |
| while( !job_received ) |
| __TBB_Yield(); |
| } |
| } else { |
| // Using extra bit of DelayMask for choosing whether to run wait_for_job or not. |
| if( DelayMask&1<<N ) { |
| rml::job* j= &ja.wait_for_job(); |
| if( j!=&job ) REPORT("%p\n",j); |
| ASSERT( j==&job, NULL ); |
| job_received = true; |
| } |
| Cover(8); |
| } |
| rml::job* j; |
| if( ja.try_plug(j) ) { |
| ASSERT( j==&job || !j, NULL ); |
| if( j ) { |
| Cover(9+is_owner); |
| ++job_destroyed; |
| } else { |
| __TBB_ASSERT( !is_owner, "owner failed to create job but plugged self" ); |
| Cover(11); |
| } |
| } else { |
| Cover(12+is_owner); |
| } |
| } |
| |
| class Loop: NoAssign { |
| State& s; |
| public: |
| Loop(State& s_) : s(s_) {} |
| void operator()( int i ) const {s.exercise(i==0);} |
| }; |
| |
| /** Return true if coverage is acceptable. |
| If report==true, issue message if it is unacceptable. */ |
| bool CheckCoverage( bool report ) { |
| bool okay = true; |
| for( int i=0; i<N; ++i ) { |
| const int min_coverage = 4; |
| if( Coverage[i]<min_coverage ) { |
| okay = false; |
| if( report ) |
| REPORT("Warning: Coverage[%d]=%d is less than acceptable minimum of %d\n", i, int(Coverage[i]),min_coverage); |
| } |
| } |
| return okay; |
| } |
| |
| int TestMain () { |
| for( DelayMask=0; DelayMask<8<<N; ++DelayMask ) { |
| State s; |
| NativeParallelFor( 2, Loop(s) ); |
| if( CheckCoverage(false) ) { |
| // Reached acceptable code coverage level |
| break; |
| } |
| } |
| CheckCoverage(true); |
| return Harness::Done; |
| } |