1+ /* ****************************************************************************
2+
3+ Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4+ more contributor license agreements. See the NOTICE file distributed
5+ with this work for additional information regarding copyright ownership.
6+ Accellera licenses this file to you under the Apache License, Version 2.0
7+ (the "License"); you may not use this file except in compliance with the
8+ License. You may obtain a copy of the License at
9+
10+ http://www.apache.org/licenses/LICENSE-2.0
11+
12+ Unless required by applicable law or agreed to in writing, software
13+ distributed under the License is distributed on an "AS IS" BASIS,
14+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15+ implied. See the License for the specific language governing
16+ permissions and limitations under the License.
17+
18+ *****************************************************************************/
19+
20+ /* ****************************************************************************
21+
22+ sc_on_context.h -- Job handler to run on context
23+
24+ Original Author: Mark burton
25+
26+ CHANGE LOG AT END OF FILE
27+ *****************************************************************************/
28+
29+ #ifndef SC_ON_CONTEXT_H
30+ #define SC_ON_CONTEXT_H
31+
32+ #include < thread>
33+ #include < mutex>
34+ #include < condition_variable>
35+ #include < queue>
36+ #include < future>
37+
38+ #include < sysc/kernel/sc_simcontext.h>
39+ #include < sysc/kernel/sc_event.h>
40+
41+ namespace sc_core {
42+
43+ class sc_on_context : public sc_core ::sc_module
44+ {
45+ protected:
46+ class AsyncJob
47+ {
48+ public:
49+ using Ptr = std::shared_ptr<AsyncJob>;
50+
51+ private:
52+ std::packaged_task<void ()> m_task;
53+
54+ bool m_cancelled = false ;
55+
56+ void run_job () { m_task (); }
57+
58+ public:
59+ AsyncJob (std::function<void ()>&& job): m_task(job) {}
60+
61+ AsyncJob (std::function<void ()>& job): m_task(job) {}
62+
63+ AsyncJob () = delete ;
64+ AsyncJob (const AsyncJob&) = delete ;
65+
66+ void operator ()() { run_job (); }
67+
68+ /* *
69+ * @brief Cancel a job
70+ *
71+ * @details Cancel a job by setting m_cancelled to true and by
72+ * resetting the task. Any waiter will then be unblocked immediately.
73+ */
74+ void cancel ()
75+ {
76+ m_cancelled = true ;
77+ m_task.reset ();
78+ }
79+
80+ void wait ()
81+ {
82+ auto future = m_task.get_future ();
83+
84+ future.wait ();
85+
86+ if (!m_cancelled) {
87+ future.get ();
88+ }
89+ }
90+
91+ bool is_cancelled () const { return m_cancelled; }
92+ };
93+
94+
95+ sc_core::sc_simcontext *m_simc=nullptr ;
96+
97+ /* Async job queue */
98+ std::queue<AsyncJob::Ptr> m_async_jobs;
99+ AsyncJob::Ptr m_running_job;
100+ std::mutex m_async_jobs_mutex;
101+
102+ sc_event m_jobs_handler_event; // NB this event must be threadsafe.
103+ std::atomic<bool > running = true ;
104+
105+ // Process inside a thread incase the job calls wait
106+ void jobs_handler ()
107+ {
108+ m_simc=sc_core::sc_get_curr_simcontext ();
109+
110+ std::unique_lock<std::mutex> lock (m_async_jobs_mutex);
111+ running = true ;
112+ for (; running;) {
113+ while (!m_async_jobs.empty ()) {
114+ m_running_job = m_async_jobs.front ();
115+ m_async_jobs.pop ();
116+
117+ lock.unlock ();
118+ sc_core::sc_unsuspendable (); // a wait in the job will cause systemc time to advance
119+ (*m_running_job)();
120+ sc_core::sc_suspendable ();
121+ lock.lock ();
122+
123+ m_running_job.reset ();
124+ }
125+ lock.unlock ();
126+ wait (m_jobs_handler_event);
127+ lock.lock ();
128+ }
129+ SC_REPORT_WARNING (" sc_on_context" , " Stopped" );
130+ sc_core::sc_stop ();
131+ }
132+
133+ void cancel_pendings_locked ()
134+ {
135+ while (!m_async_jobs.empty ()) {
136+ m_async_jobs.front ()->cancel ();
137+ m_async_jobs.pop ();
138+ }
139+ }
140+
141+ public:
142+ sc_on_context (const sc_core::sc_module_name& n = sc_core::sc_module_name(" sc_on_ctx" ))
143+ : sc_module(n)
144+ {
145+ SC_THREAD (jobs_handler);
146+ }
147+
148+ /* *
149+ * @brief Cancel all pending jobs
150+ *
151+ * @detail Cancel all the pending jobs. The callers will be unblocked
152+ * if they are waiting for the job.
153+ */
154+ void cancel_pendings ()
155+ {
156+ std::lock_guard<std::mutex> lock (m_async_jobs_mutex);
157+
158+ cancel_pendings_locked ();
159+ }
160+
161+ /* *
162+ * @brief Cancel all pending and running jobs
163+ *
164+ * @detail Cancel all the pending jobs and the currently running job.
165+ * The callers will be unblocked if they are waiting for the
166+ * job. Note that if the currently running job is resumed, the
167+ * behaviour is undefined. This method is meant to be called
168+ * after simulation has ended.
169+ */
170+ void cancel_all ()
171+ {
172+ std::lock_guard<std::mutex> lock (m_async_jobs_mutex);
173+
174+ cancel_pendings_locked ();
175+
176+ if (m_running_job) {
177+ m_running_job->cancel ();
178+ m_running_job.reset ();
179+ }
180+ }
181+ void stop ()
182+ {
183+ running = false ;
184+ m_jobs_handler_event.notify ();
185+ }
186+
187+ void end_of_simulation ()
188+ {
189+ running = false ;
190+ cancel_all ();
191+ }
192+
193+ void fork (std::function<void ()> job_entry) { run (job_entry, false ); }
194+
195+ /* *
196+ * @brief Run a job on the SystemC kernel thread
197+ *
198+ * @param[in] job_entry The job to run
199+ * @param[in] wait If true, wait for job completion
200+ *
201+ * @return true if the job has been succesfully executed or if `wait`
202+ * was false, false if it has been cancelled (see
203+ * `Sc_on_context::cancel_all`).
204+ */
205+ bool run (std::function<void ()> job_entry, bool wait = true)
206+ {
207+ if (!running) return false ;
208+ if (is_on_sysc ()) {
209+ job_entry ();
210+ return true ;
211+ } else {
212+ AsyncJob::Ptr job (new AsyncJob (job_entry));
213+
214+ {
215+ std::lock_guard<std::mutex> lock (m_async_jobs_mutex);
216+ m_async_jobs.push (job);
217+ }
218+
219+ m_jobs_handler_event.notify ();
220+
221+ if (wait) {
222+ /* Wait for job completion */
223+ try {
224+ job->wait ();
225+ } catch (std::runtime_error const & e) {
226+ /* Report unknown runtime errors, without causing a futher excetion */
227+ auto old = sc_core::sc_report_handler::set_actions (sc_core::SC_ERROR,
228+ sc_core::SC_LOG | sc_core::SC_DISPLAY);
229+ SC_REPORT_ERROR (
230+ " Sc_on_context" ,
231+ (" Run on systemc received a runtime error from job: " + std::string (e.what ())).c_str ());
232+ sc_core::sc_report_handler::set_actions (sc_core::SC_ERROR, old);
233+ stop ();
234+ return false ;
235+ } catch (const std::exception& exc) {
236+ if (sc_core::sc_report_handler::get_count (sc_core::SC_ERROR) == 0 ) {
237+ /* Report exceptions that were not caused by SC_ERRORS (which have already been reported)*/
238+ auto old = sc_core::sc_report_handler::set_actions (sc_core::SC_ERROR,
239+ sc_core::SC_LOG | sc_core::SC_DISPLAY);
240+ SC_REPORT_ERROR (
241+ " Sc_on_context" ,
242+ (" Run on systemc received an exception from job: " + std::string (exc.what ())).c_str ());
243+ sc_core::sc_report_handler::set_actions (sc_core::SC_ERROR, old);
244+ }
245+ stop ();
246+ return false ;
247+ } catch (...) {
248+ auto old = sc_core::sc_report_handler::set_actions (sc_core::SC_ERROR,
249+ sc_core::SC_LOG | sc_core::SC_DISPLAY);
250+ SC_REPORT_ERROR (" Sc_on_context" , " Run on systemc received an unknown exception from job" );
251+ sc_core::sc_report_handler::set_actions (sc_core::SC_ERROR, old);
252+ stop ();
253+ return false ;
254+ }
255+
256+ return !job->is_cancelled ();
257+ }
258+
259+ return true ;
260+ }
261+ }
262+
263+ /* *
264+ * @return Whether we are on the right SystemC context
265+ */
266+ bool is_on_sysc () const { return sc_core::sc_get_curr_simcontext ()==m_simc; }
267+ };
268+ }
269+ #endif // SC_ON_CONTEXT_H
0 commit comments