Jump to content
jsacacia

how do I terminate all threads at end of simulation ?

Recommended Posts

Hi All,

I am calling sc_stop() and the end of my simulation but this does not seem to terminate all threads

(I am using quickthreads) and therefore Valgrind claims there is a memory leak.

Is there a way to tell all threads to terminate ?

Thanks,

Jon

Share this post


Link to post
Share on other sites

Hi all,

Here is an example for terminating a thread. It spawns a dynamic thread which runs an infinite while loop. The simulation is controlled by a static thread, which will throw an exception in the dynamic thread at the end of the simulation to ensure its termination:

#define SC_INCLUDE_DYNAMIC_PROCESSES
#include <systemc.h>

struct TestClass {
    TestClass()
    : ptr ( (new int)+1 ) // store not the address of actually allocated memory, but addr+1.
    // This is to confuse valgrind- otherwise it counts those leaks as "still reachable".
    {cout << "construct TestClass object" << endl; set(0);};
    ~TestClass() {cout << "destruct TestClass object" << endl; delete (ptr-1);};
    int get() { return *(ptr-1); };
    void set(int newVal) { *(ptr-1) = newVal; }; 
    int* ptr;
};

void dynamicThread(sc_event& ev) {
  cout << "start dynamic thread" << endl;
  TestClass test;
  while(1) {
    cout << "  dynamic " << test.get() << endl;
    test.set( test.get()+1 );
    wait (ev);  
  }
}

SC_MODULE(Module) {
  void staticThread() {
    cout << "start static thread" << endl;
    sc_event ev;
    sc_process_handle h = sc_spawn( sc_bind(&dynamicThread, sc_ref(ev)) );
    for (unsigned i(0); i<3; ++i) {
        cout << "  static " << i << endl;
        ev.notify();        
        wait(10, SC_NS);
    }
    h.throw_it( sc_halt() );
  }

  SC_CTOR(Module) {
     SC_THREAD(staticThread)
 }
};

int sc_main (int argc , char *argv[])
{
  Module module("module");
  sc_start();
  return 0;
}

The above code creates some errors in Valgrind which can be suppressed with these settings (I tried both SystemC-2.3.1 and 2.3.2- they create the same number of false-positives (11) for me):

{
   SystemC 1 sc_core::sc_object_manager: needed for 2.3.1 and 2.3.2 - see http://forums.accellera.org/topic/113-systemc-23valgrind-err or/
   Memcheck:Leak
   fun:_Znwm
   fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
   ...
   fun:_ZN7sc_core17sc_object_manager11create_nameEPKc
}


{
   SystemC 1 pthread_create@@GLIBC_2.2.5: needed for both 2.3.1 and 2.3.2  see http://forums.accellera.org/topic/113-systemc-23valgrind-err or/
   Memcheck:Leak
   fun:calloc
   fun:_dl_allocate_tls
   fun:pthread_create@@GLIBC_2.2.5
   fun:_ZN7sc_core18sc_cor_pkg_pthread6createEmPFvPvES1_
   fun:_ZN7sc_core17sc_thread_process22prepare_for_simulationEv
}


{
   SystemC 1 sc_core::sc_event::register_event: needed for 2.3.2 - see http://forums.accellera.org/topic/113-systemc-23valgrind-err or/
   Memcheck:Leak
   fun:_Znwm
   fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
   obj:/usr/lib64/libstdc++.so.6.0.13
   fun:_ZNSsC1IPcEET_S1_RKSaIcE
   fun:_ZNKSt15basic_stringbufIcSt11char_traitsIcESaIcEE3strEv
   fun:_ZN7sc_core11sc_name_gen15gen_unique_nameEPKcb
   fun:_ZN7sc_core8sc_event14register_eventEPKcb
}

 

The program outputs:

start static thread
  static 0
start dynamic thread
construct TestClass object
  dynamic 0
  static 1
  dynamic 1
  static 2
  dynamic 2
destruct TestClass object
Terminating process module.staticThread.thread_p_0

 

If I remove

    h.throw_it( sc_halt() );

from the example above, then the destructor of TestClass does not get executed (program stops after "dynamic2"), and Valgrind (correctly) reports a leak:

==599== 4 bytes in 1 blocks are definitely lost in loss record 5 of 74
==599==    at 0x4A075FC: operator new(unsigned long) (vg_replace_malloc.c:298)
==599==    by 0x41F0DF: TestClass::TestClass() (main.cpp:10)
==599==    by 0x41EBF6: dynamicThread(sc_core::sc_event&) (main.cpp:21)
==599==    by 0x41F973: void sc_boost::_bi::list1<sc_boost::reference_wrapper<sc_core::sc_event> >::operator()<void (*)(sc_core::sc_event&), sc_boost::_bi::list0>(sc_boost::_bi::type<void>, void (*&)(sc_core::sc_event&), sc_boost::_bi::list0&, int) (bind.hpp:229)
==599==    by 0x41F926: sc_boost::_bi::bind_t<void, void (*)(sc_core::sc_event&), sc_boost::_bi::list1<sc_boost::reference_wrapper<sc_core::sc_event> > >::operator()() (bind_template.hpp:20)
==599==    by 0x41F8D7: sc_core::sc_spawn_object<sc_boost::_bi::bind_t<void, void (*)(sc_core::sc_event&), sc_boost::_bi::list1<sc_boost::reference_wrapper<sc_core::sc_event> > > >::semantics() (sc_spawn.h:83)
==599==    by 0x441C05: sc_core::sc_thread_cor_fn(void*) (sc_process.h:677)
==599==    by 0x4493EA: sc_core::sc_cor_pthread::invoke_module_method(void*) (sc_cor_pthread.cpp:127)
==599==    by 0x3BC1407AA0: start_thread (in /lib64/libpthread-2.12.so)
==599==    by 0x3BC0CE8AAC: clone (in /lib64/libc-2.12.so)

 

Is there a better way to achieve termination of such (dynamic) threads with infinite loops?

The above method means that the user needs to keep track of all handles to spawned processes that have not yet terminated. Is there a list of theses handles in the kernel that can be easily accessed?

Regards,
Jonas

Share this post


Link to post
Share on other sites

Ok, now I understand your concern: It's also about stack-allocated variables in your threads that are not cleaned up at the end of the simulation, as their stacks are not unwound and dynamic process stacks may not even be explicitly deallocated.  I'm not sure, if these cases qualify as real memory leaks, though.

Small comment: instead of throwing an (non-standard) sc_halt object, you can simply call :

 h.kill(SC_INCLUDE_DESCENDANTS); // also kills all child processes

There is currently no way to obtain (or kill) all running processes in the simulation, but you can roll your own function to

  • recurse over the object hierarchy via get_child_objects()
  • convert objects to process handles, if valid() and !terminated() call kill(SC_INCLUDE_DESCENDANTS) 

/Philipp

Share this post


Link to post
Share on other sites

Here the previous example but it uses the method suggested by Philipp to terminate all running threads:

#define SC_INCLUDE_DYNAMIC_PROCESSES
#include <systemc.h>

struct TestClass {
    TestClass()
    : ptr ( (new int)+1 ) // store not the address of actually allocated memory, but addr+1.
    // This is to confuse valgrind- otherwise it counts those leaks as "still reachable".
    {cout << "construct TestClass object" << endl; set(0);};
    ~TestClass() {cout << "destruct TestClass object" << endl; delete (ptr-1);};
    int get() { return *(ptr-1); };
    void set(int newVal) { *(ptr-1) = newVal; }; 
    int* ptr;
};


void dynamicThread2(sc_event& ev) {
  cout << "start dynamic thread2" << endl;
  TestClass test;
  while(1) {
    wait (ev);  
  }
  cout << "end dynamic thread2" << endl;
}

void dynamicThread(sc_event& ev) {
  cout << "start dynamic thread" << endl;
  TestClass test;
  while(1) {
    cout << "  dynamic " << test.get() << endl;
    sc_spawn( sc_bind(&dynamicThread2, sc_ref(ev)) );
    test.set( test.get()+1 );
    wait (ev);  
  }
}

void killAllRunningChildProcesses(sc_process_handle& h)
{
      const ::std::vector<sc_object*>& children = h.get_child_objects();
      for (unsigned i(0); i<children.size(); ++i) {
          // convert objects to process handles, if valid() and !terminated() call kill(SC_INCLUDE_DESCENDANTS) 
          sc_process_handle h = sc_process_handle(children.at(i));
          if (h.valid()) {
              if (!h.terminated()) {
                  cout << "Killing thread: " << h.name() << endl;
                  h.kill(SC_INCLUDE_DESCENDANTS);
              }
          }
      }    
}


SC_MODULE(Module) {
  void staticThread() {
    cout << "start static thread" << endl;
    sc_event ev;
    sc_spawn( sc_bind(&dynamicThread, sc_ref(ev)) );
    for (unsigned i(0); i<3; ++i) {
        cout << "  static " << i << endl;
        ev.notify();        
        wait(10, SC_NS);
    }

    sc_process_handle h = sc_get_current_process_handle();
    cout << "Current process: " << sc_process_handle(h).name() << endl;
    cout << "Now walk through children\n";
    killAllRunningChildProcesses(h);
    cout << "All done!\n";
  
  }

  SC_CTOR(Module) {
     SC_THREAD(staticThread)
 }
};

int sc_main (int argc , char *argv[])
{
  Module module("module");
  sc_start();
  return 0;
}

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×