jsacacia Posted October 23, 2017 Report Posted October 23, 2017 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 Quote
maehne Posted October 23, 2017 Report Posted October 23, 2017 The memory leaks reported by valgrind, which are related to quickthreads are likely false positives, as is explained by Philipp A. Hartmann in this older thread "SystemC 2.3: valgrind error". Regards, Torsten Quote
Philipp A Hartmann Posted October 24, 2017 Report Posted October 24, 2017 While most of the things in the referenced discussion still hold, we slightly extended the "thread cleanup" in the recently released SystemC 2.3.2. Can you please try, if the behavior improves when using the new version? Thanks, Philipp Quote
Jonas Posted October 26, 2017 Report Posted October 26, 2017 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 Quote
Philipp A Hartmann Posted October 26, 2017 Report Posted October 26, 2017 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 Quote
Jonas Posted October 26, 2017 Report Posted October 26, 2017 That's very helpful. Thanks Philipp! Quote
Jonas Posted October 27, 2017 Report Posted October 27, 2017 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; } Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.